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();
2154 * This not working on ios
2156 // if(this.el.getStyle('top').slice(-1) != "%"){
2157 // this.el.setXY(xy);
2160 if(!isNaN(this.el.getStyle('top') * 1)){
2165 this.fireEvent("show", this);
2171 this.doFocus.defer(50, this);
2175 doFocus : function(){
2177 this.focusEl.focus();
2182 * Hides this menu and optionally all parent menus
2183 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2185 hide : function(deep)
2188 this.hideMenuItems();
2189 if(this.el && this.isVisible()){
2190 this.fireEvent("beforehide", this);
2191 if(this.activeItem){
2192 this.activeItem.deactivate();
2193 this.activeItem = null;
2195 this.triggerEl.removeClass('open');;
2197 this.fireEvent("hide", this);
2199 if(deep === true && this.parentMenu){
2200 this.parentMenu.hide(true);
2204 onTriggerClick : function(e)
2206 Roo.log('trigger click');
2208 var target = e.getTarget();
2210 Roo.log(target.nodeName.toLowerCase());
2212 if(target.nodeName.toLowerCase() === 'i'){
2218 onTriggerPress : function(e)
2220 Roo.log('trigger press');
2221 //Roo.log(e.getTarget());
2222 // Roo.log(this.triggerEl.dom);
2224 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2225 var pel = Roo.get(e.getTarget());
2226 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2227 Roo.log('is treeview or dropdown?');
2231 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2235 if (this.isVisible()) {
2240 this.show(this.triggerEl, false, false);
2243 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2250 hideMenuItems : function()
2252 Roo.log("hide Menu Items");
2256 //$(backdrop).remove()
2257 this.el.select('.open',true).each(function(aa) {
2259 aa.removeClass('open');
2260 //var parent = getParent($(this))
2261 //var relatedTarget = { relatedTarget: this }
2263 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2264 //if (e.isDefaultPrevented()) return
2265 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2268 addxtypeChild : function (tree, cntr) {
2269 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2271 this.menuitems.add(comp);
2292 * @class Roo.bootstrap.MenuItem
2293 * @extends Roo.bootstrap.Component
2294 * Bootstrap MenuItem class
2295 * @cfg {String} html the menu label
2296 * @cfg {String} href the link
2297 * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2298 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2299 * @cfg {Boolean} active used on sidebars to highlight active itesm
2300 * @cfg {String} fa favicon to show on left of menu item.
2301 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2305 * Create a new MenuItem
2306 * @param {Object} config The config object
2310 Roo.bootstrap.MenuItem = function(config){
2311 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2316 * The raw click event for the entire grid.
2317 * @param {Roo.bootstrap.MenuItem} this
2318 * @param {Roo.EventObject} e
2324 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2328 preventDefault: true,
2329 isContainer : false,
2333 getAutoCreate : function(){
2335 if(this.isContainer){
2338 cls: 'dropdown-menu-item'
2352 if (this.fa !== false) {
2355 cls : 'fa fa-' + this.fa
2364 cls: 'dropdown-menu-item',
2367 if (this.parent().type == 'treeview') {
2368 cfg.cls = 'treeview-menu';
2371 cfg.cls += ' active';
2376 anc.href = this.href || cfg.cn[0].href ;
2377 ctag.html = this.html || cfg.cn[0].html ;
2381 initEvents: function()
2383 if (this.parent().type == 'treeview') {
2384 this.el.select('a').on('click', this.onClick, this);
2387 this.menu.parentType = this.xtype;
2388 this.menu.triggerEl = this.el;
2389 this.menu = this.addxtype(Roo.apply({}, this.menu));
2393 onClick : function(e)
2395 Roo.log('item on click ');
2396 //if(this.preventDefault){
2397 // e.preventDefault();
2399 //this.parent().hideMenuItems();
2401 this.fireEvent('click', this, e);
2420 * @class Roo.bootstrap.MenuSeparator
2421 * @extends Roo.bootstrap.Component
2422 * Bootstrap MenuSeparator class
2425 * Create a new MenuItem
2426 * @param {Object} config The config object
2430 Roo.bootstrap.MenuSeparator = function(config){
2431 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2434 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2436 getAutoCreate : function(){
2455 * @class Roo.bootstrap.Modal
2456 * @extends Roo.bootstrap.Component
2457 * Bootstrap Modal class
2458 * @cfg {String} title Title of dialog
2459 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2460 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2461 * @cfg {Boolean} specificTitle default false
2462 * @cfg {Array} buttons Array of buttons or standard button set..
2463 * @cfg {String} buttonPosition (left|right|center) default right
2464 * @cfg {Boolean} animate default true
2465 * @cfg {Boolean} allow_close default true
2466 * @cfg {Boolean} fitwindow default true
2470 * Create a new Modal Dialog
2471 * @param {Object} config The config object
2474 Roo.bootstrap.Modal = function(config){
2475 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2480 * The raw btnclick event for the button
2481 * @param {Roo.EventObject} e
2485 this.buttons = this.buttons || [];
2488 this.tmpl = Roo.factory(this.tmpl);
2493 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2495 title : 'test dialog',
2505 specificTitle: false,
2507 buttonPosition: 'right',
2524 onRender : function(ct, position)
2526 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2529 var cfg = Roo.apply({}, this.getAutoCreate());
2532 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2534 //if (!cfg.name.length) {
2538 cfg.cls += ' ' + this.cls;
2541 cfg.style = this.style;
2543 this.el = Roo.get(document.body).createChild(cfg, position);
2545 //var type = this.el.dom.type;
2548 if(this.tabIndex !== undefined){
2549 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2552 this.dialogEl = this.el.select('.modal-dialog',true).first();
2553 this.bodyEl = this.el.select('.modal-body',true).first();
2554 this.closeEl = this.el.select('.modal-header .close', true).first();
2555 this.footerEl = this.el.select('.modal-footer',true).first();
2556 this.titleEl = this.el.select('.modal-title',true).first();
2560 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2561 this.maskEl.enableDisplayMode("block");
2563 //this.el.addClass("x-dlg-modal");
2565 if (this.buttons.length) {
2566 Roo.each(this.buttons, function(bb) {
2567 var b = Roo.apply({}, bb);
2568 b.xns = b.xns || Roo.bootstrap;
2569 b.xtype = b.xtype || 'Button';
2570 if (typeof(b.listeners) == 'undefined') {
2571 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2574 var btn = Roo.factory(b);
2576 btn.render(this.el.select('.modal-footer div').first());
2580 // render the children.
2583 if(typeof(this.items) != 'undefined'){
2584 var items = this.items;
2587 for(var i =0;i < items.length;i++) {
2588 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2592 this.items = nitems;
2594 // where are these used - they used to be body/close/footer
2598 //this.el.addClass([this.fieldClass, this.cls]);
2602 getAutoCreate : function(){
2607 html : this.html || ''
2612 cls : 'modal-title',
2616 if(this.specificTitle){
2622 if (this.allow_close) {
2633 style : 'display: none',
2636 cls: "modal-dialog",
2639 cls : "modal-content",
2642 cls : 'modal-header',
2647 cls : 'modal-footer',
2651 cls: 'btn-' + this.buttonPosition
2668 modal.cls += ' fade';
2674 getChildContainer : function() {
2679 getButtonContainer : function() {
2680 return this.el.select('.modal-footer div',true).first();
2683 initEvents : function()
2685 if (this.allow_close) {
2686 this.closeEl.on('click', this.hide, this);
2688 Roo.EventManager.onWindowResize(this.resize, this, true);
2695 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2696 if (this.fitwindow) {
2697 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2698 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 30;
2703 setSize : function(w,h)
2713 if (!this.rendered) {
2717 this.el.setStyle('display', 'block');
2719 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2722 this.el.addClass('in');
2725 this.el.addClass('in');
2729 // not sure how we can show data in here..
2731 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2734 Roo.get(document.body).addClass("x-body-masked");
2735 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2737 this.el.setStyle('zIndex', '10001');
2739 this.fireEvent('show', this);
2740 this.items.forEach(function(e) {
2741 e.layout ? e.layout() : false;
2752 Roo.get(document.body).removeClass("x-body-masked");
2753 this.el.removeClass('in');
2754 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2756 if(this.animate){ // why
2758 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2760 this.el.setStyle('display', 'none');
2763 this.fireEvent('hide', this);
2766 addButton : function(str, cb)
2770 var b = Roo.apply({}, { html : str } );
2771 b.xns = b.xns || Roo.bootstrap;
2772 b.xtype = b.xtype || 'Button';
2773 if (typeof(b.listeners) == 'undefined') {
2774 b.listeners = { click : cb.createDelegate(this) };
2777 var btn = Roo.factory(b);
2779 btn.render(this.el.select('.modal-footer div').first());
2785 setDefaultButton : function(btn)
2787 //this.el.select('.modal-footer').()
2791 resizeTo: function(w,h)
2795 this.dialogEl.setWidth(w);
2796 if (this.diff === false) {
2797 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2800 this.bodyEl.setHeight(h-this.diff);
2804 setContentSize : function(w, h)
2808 onButtonClick: function(btn,e)
2811 this.fireEvent('btnclick', btn.name, e);
2814 * Set the title of the Dialog
2815 * @param {String} str new Title
2817 setTitle: function(str) {
2818 this.titleEl.dom.innerHTML = str;
2821 * Set the body of the Dialog
2822 * @param {String} str new Title
2824 setBody: function(str) {
2825 this.bodyEl.dom.innerHTML = str;
2828 * Set the body of the Dialog using the template
2829 * @param {Obj} data - apply this data to the template and replace the body contents.
2831 applyBody: function(obj)
2834 Roo.log("Error - using apply Body without a template");
2837 this.tmpl.overwrite(this.bodyEl, obj);
2843 Roo.apply(Roo.bootstrap.Modal, {
2845 * Button config that displays a single OK button
2854 * Button config that displays Yes and No buttons
2870 * Button config that displays OK and Cancel buttons
2885 * Button config that displays Yes, No and Cancel buttons
2908 * messagebox - can be used as a replace
2912 * @class Roo.MessageBox
2913 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2917 Roo.Msg.alert('Status', 'Changes saved successfully.');
2919 // Prompt for user data:
2920 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2922 // process text value...
2926 // Show a dialog using config options:
2928 title:'Save Changes?',
2929 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2930 buttons: Roo.Msg.YESNOCANCEL,
2937 Roo.bootstrap.MessageBox = function(){
2938 var dlg, opt, mask, waitTimer;
2939 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2940 var buttons, activeTextEl, bwidth;
2944 var handleButton = function(button){
2946 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2950 var handleHide = function(){
2952 dlg.el.removeClass(opt.cls);
2955 // Roo.TaskMgr.stop(waitTimer);
2956 // waitTimer = null;
2961 var updateButtons = function(b){
2964 buttons["ok"].hide();
2965 buttons["cancel"].hide();
2966 buttons["yes"].hide();
2967 buttons["no"].hide();
2968 //dlg.footer.dom.style.display = 'none';
2971 dlg.footerEl.dom.style.display = '';
2972 for(var k in buttons){
2973 if(typeof buttons[k] != "function"){
2976 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2977 width += buttons[k].el.getWidth()+15;
2987 var handleEsc = function(d, k, e){
2988 if(opt && opt.closable !== false){
2998 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2999 * @return {Roo.BasicDialog} The BasicDialog element
3001 getDialog : function(){
3003 dlg = new Roo.bootstrap.Modal( {
3006 //constraintoviewport:false,
3008 //collapsible : false,
3013 //buttonAlign:"center",
3014 closeClick : function(){
3015 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3018 handleButton("cancel");
3023 dlg.on("hide", handleHide);
3025 //dlg.addKeyListener(27, handleEsc);
3027 this.buttons = buttons;
3028 var bt = this.buttonText;
3029 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3030 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3031 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3032 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3034 bodyEl = dlg.bodyEl.createChild({
3036 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3037 '<textarea class="roo-mb-textarea"></textarea>' +
3038 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3040 msgEl = bodyEl.dom.firstChild;
3041 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3042 textboxEl.enableDisplayMode();
3043 textboxEl.addKeyListener([10,13], function(){
3044 if(dlg.isVisible() && opt && opt.buttons){
3047 }else if(opt.buttons.yes){
3048 handleButton("yes");
3052 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3053 textareaEl.enableDisplayMode();
3054 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3055 progressEl.enableDisplayMode();
3056 var pf = progressEl.dom.firstChild;
3058 pp = Roo.get(pf.firstChild);
3059 pp.setHeight(pf.offsetHeight);
3067 * Updates the message box body text
3068 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3069 * the XHTML-compliant non-breaking space character '&#160;')
3070 * @return {Roo.MessageBox} This message box
3072 updateText : function(text){
3073 if(!dlg.isVisible() && !opt.width){
3074 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3076 msgEl.innerHTML = text || ' ';
3078 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3079 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3081 Math.min(opt.width || cw , this.maxWidth),
3082 Math.max(opt.minWidth || this.minWidth, bwidth)
3085 activeTextEl.setWidth(w);
3087 if(dlg.isVisible()){
3088 dlg.fixedcenter = false;
3090 // to big, make it scroll. = But as usual stupid IE does not support
3093 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3094 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3095 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3097 bodyEl.dom.style.height = '';
3098 bodyEl.dom.style.overflowY = '';
3101 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3103 bodyEl.dom.style.overflowX = '';
3106 dlg.setContentSize(w, bodyEl.getHeight());
3107 if(dlg.isVisible()){
3108 dlg.fixedcenter = true;
3114 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3115 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3116 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3117 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3118 * @return {Roo.MessageBox} This message box
3120 updateProgress : function(value, text){
3122 this.updateText(text);
3124 if (pp) { // weird bug on my firefox - for some reason this is not defined
3125 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3131 * Returns true if the message box is currently displayed
3132 * @return {Boolean} True if the message box is visible, else false
3134 isVisible : function(){
3135 return dlg && dlg.isVisible();
3139 * Hides the message box if it is displayed
3142 if(this.isVisible()){
3148 * Displays a new message box, or reinitializes an existing message box, based on the config options
3149 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3150 * The following config object properties are supported:
3152 Property Type Description
3153 ---------- --------------- ------------------------------------------------------------------------------------
3154 animEl String/Element An id or Element from which the message box should animate as it opens and
3155 closes (defaults to undefined)
3156 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3157 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3158 closable Boolean False to hide the top-right close button (defaults to true). Note that
3159 progress and wait dialogs will ignore this property and always hide the
3160 close button as they can only be closed programmatically.
3161 cls String A custom CSS class to apply to the message box element
3162 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3163 displayed (defaults to 75)
3164 fn Function A callback function to execute after closing the dialog. The arguments to the
3165 function will be btn (the name of the button that was clicked, if applicable,
3166 e.g. "ok"), and text (the value of the active text field, if applicable).
3167 Progress and wait dialogs will ignore this option since they do not respond to
3168 user actions and can only be closed programmatically, so any required function
3169 should be called by the same code after it closes the dialog.
3170 icon String A CSS class that provides a background image to be used as an icon for
3171 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3172 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3173 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3174 modal Boolean False to allow user interaction with the page while the message box is
3175 displayed (defaults to true)
3176 msg String A string that will replace the existing message box body text (defaults
3177 to the XHTML-compliant non-breaking space character ' ')
3178 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3179 progress Boolean True to display a progress bar (defaults to false)
3180 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3181 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3182 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3183 title String The title text
3184 value String The string value to set into the active textbox element if displayed
3185 wait Boolean True to display a progress bar (defaults to false)
3186 width Number The width of the dialog in pixels
3193 msg: 'Please enter your address:',
3195 buttons: Roo.MessageBox.OKCANCEL,
3198 animEl: 'addAddressBtn'
3201 * @param {Object} config Configuration options
3202 * @return {Roo.MessageBox} This message box
3204 show : function(options)
3207 // this causes nightmares if you show one dialog after another
3208 // especially on callbacks..
3210 if(this.isVisible()){
3213 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3214 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3215 Roo.log("New Dialog Message:" + options.msg )
3216 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3217 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3220 var d = this.getDialog();
3222 d.setTitle(opt.title || " ");
3223 d.closeEl.setDisplayed(opt.closable !== false);
3224 activeTextEl = textboxEl;
3225 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3230 textareaEl.setHeight(typeof opt.multiline == "number" ?
3231 opt.multiline : this.defaultTextHeight);
3232 activeTextEl = textareaEl;
3241 progressEl.setDisplayed(opt.progress === true);
3242 this.updateProgress(0);
3243 activeTextEl.dom.value = opt.value || "";
3245 dlg.setDefaultButton(activeTextEl);
3247 var bs = opt.buttons;
3251 }else if(bs && bs.yes){
3252 db = buttons["yes"];
3254 dlg.setDefaultButton(db);
3256 bwidth = updateButtons(opt.buttons);
3257 this.updateText(opt.msg);
3259 d.el.addClass(opt.cls);
3261 d.proxyDrag = opt.proxyDrag === true;
3262 d.modal = opt.modal !== false;
3263 d.mask = opt.modal !== false ? mask : false;
3265 // force it to the end of the z-index stack so it gets a cursor in FF
3266 document.body.appendChild(dlg.el.dom);
3267 d.animateTarget = null;
3268 d.show(options.animEl);
3274 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3275 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3276 * and closing the message box when the process is complete.
3277 * @param {String} title The title bar text
3278 * @param {String} msg The message box body text
3279 * @return {Roo.MessageBox} This message box
3281 progress : function(title, msg){
3288 minWidth: this.minProgressWidth,
3295 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3296 * If a callback function is passed it will be called after the user clicks the button, and the
3297 * id of the button that was clicked will be passed as the only parameter to the callback
3298 * (could also be the top-right close button).
3299 * @param {String} title The title bar text
3300 * @param {String} msg The message box body text
3301 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3302 * @param {Object} scope (optional) The scope of the callback function
3303 * @return {Roo.MessageBox} This message box
3305 alert : function(title, msg, fn, scope){
3318 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3319 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3320 * You are responsible for closing the message box when the process is complete.
3321 * @param {String} msg The message box body text
3322 * @param {String} title (optional) The title bar text
3323 * @return {Roo.MessageBox} This message box
3325 wait : function(msg, title){
3336 waitTimer = Roo.TaskMgr.start({
3338 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3346 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3347 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3348 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3349 * @param {String} title The title bar text
3350 * @param {String} msg The message box body text
3351 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3352 * @param {Object} scope (optional) The scope of the callback function
3353 * @return {Roo.MessageBox} This message box
3355 confirm : function(title, msg, fn, scope){
3359 buttons: this.YESNO,
3368 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3369 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3370 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3371 * (could also be the top-right close button) and the text that was entered will be passed as the two
3372 * parameters to the callback.
3373 * @param {String} title The title bar text
3374 * @param {String} msg The message box body text
3375 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3376 * @param {Object} scope (optional) The scope of the callback function
3377 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3378 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3379 * @return {Roo.MessageBox} This message box
3381 prompt : function(title, msg, fn, scope, multiline){
3385 buttons: this.OKCANCEL,
3390 multiline: multiline,
3397 * Button config that displays a single OK button
3402 * Button config that displays Yes and No buttons
3405 YESNO : {yes:true, no:true},
3407 * Button config that displays OK and Cancel buttons
3410 OKCANCEL : {ok:true, cancel:true},
3412 * Button config that displays Yes, No and Cancel buttons
3415 YESNOCANCEL : {yes:true, no:true, cancel:true},
3418 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3421 defaultTextHeight : 75,
3423 * The maximum width in pixels of the message box (defaults to 600)
3428 * The minimum width in pixels of the message box (defaults to 100)
3433 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3434 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3437 minProgressWidth : 250,
3439 * An object containing the default button text strings that can be overriden for localized language support.
3440 * Supported properties are: ok, cancel, yes and no.
3441 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3454 * Shorthand for {@link Roo.MessageBox}
3456 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3457 Roo.Msg = Roo.Msg || Roo.MessageBox;
3466 * @class Roo.bootstrap.Navbar
3467 * @extends Roo.bootstrap.Component
3468 * Bootstrap Navbar class
3471 * Create a new Navbar
3472 * @param {Object} config The config object
3476 Roo.bootstrap.Navbar = function(config){
3477 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3481 * @event beforetoggle
3482 * Fire before toggle the menu
3483 * @param {Roo.EventObject} e
3485 "beforetoggle" : true
3489 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3498 getAutoCreate : function(){
3501 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3505 initEvents :function ()
3507 //Roo.log(this.el.select('.navbar-toggle',true));
3508 this.el.select('.navbar-toggle',true).on('click', function() {
3509 if(this.fireEvent('beforetoggle', this) !== false){
3510 this.el.select('.navbar-collapse',true).toggleClass('in');
3520 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3522 var size = this.el.getSize();
3523 this.maskEl.setSize(size.width, size.height);
3524 this.maskEl.enableDisplayMode("block");
3533 getChildContainer : function()
3535 if (this.el.select('.collapse').getCount()) {
3536 return this.el.select('.collapse',true).first();
3569 * @class Roo.bootstrap.NavSimplebar
3570 * @extends Roo.bootstrap.Navbar
3571 * Bootstrap Sidebar class
3573 * @cfg {Boolean} inverse is inverted color
3575 * @cfg {String} type (nav | pills | tabs)
3576 * @cfg {Boolean} arrangement stacked | justified
3577 * @cfg {String} align (left | right) alignment
3579 * @cfg {Boolean} main (true|false) main nav bar? default false
3580 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3582 * @cfg {String} tag (header|footer|nav|div) default is nav
3588 * Create a new Sidebar
3589 * @param {Object} config The config object
3593 Roo.bootstrap.NavSimplebar = function(config){
3594 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3597 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3613 getAutoCreate : function(){
3617 tag : this.tag || 'div',
3630 this.type = this.type || 'nav';
3631 if (['tabs','pills'].indexOf(this.type)!==-1) {
3632 cfg.cn[0].cls += ' nav-' + this.type
3636 if (this.type!=='nav') {
3637 Roo.log('nav type must be nav/tabs/pills')
3639 cfg.cn[0].cls += ' navbar-nav'
3645 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3646 cfg.cn[0].cls += ' nav-' + this.arrangement;
3650 if (this.align === 'right') {
3651 cfg.cn[0].cls += ' navbar-right';
3655 cfg.cls += ' navbar-inverse';
3682 * @class Roo.bootstrap.NavHeaderbar
3683 * @extends Roo.bootstrap.NavSimplebar
3684 * Bootstrap Sidebar class
3686 * @cfg {String} brand what is brand
3687 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3688 * @cfg {String} brand_href href of the brand
3689 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3690 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3691 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3692 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3695 * Create a new Sidebar
3696 * @param {Object} config The config object
3700 Roo.bootstrap.NavHeaderbar = function(config){
3701 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3705 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3712 desktopCenter : false,
3715 getAutoCreate : function(){
3718 tag: this.nav || 'nav',
3725 if (this.desktopCenter) {
3726 cn.push({cls : 'container', cn : []});
3733 cls: 'navbar-header',
3738 cls: 'navbar-toggle',
3739 'data-toggle': 'collapse',
3744 html: 'Toggle navigation'
3766 cls: 'collapse navbar-collapse',
3770 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3772 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3773 cfg.cls += ' navbar-' + this.position;
3775 // tag can override this..
3777 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3780 if (this.brand !== '') {
3783 href: this.brand_href ? this.brand_href : '#',
3784 cls: 'navbar-brand',
3792 cfg.cls += ' main-nav';
3800 getHeaderChildContainer : function()
3802 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3803 return this.el.select('.navbar-header',true).first();
3806 return this.getChildContainer();
3810 initEvents : function()
3812 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3814 if (this.autohide) {
3819 Roo.get(document).on('scroll',function(e) {
3820 var ns = Roo.get(document).getScroll().top;
3821 var os = prevScroll;
3825 ft.removeClass('slideDown');
3826 ft.addClass('slideUp');
3829 ft.removeClass('slideUp');
3830 ft.addClass('slideDown');
3851 * @class Roo.bootstrap.NavSidebar
3852 * @extends Roo.bootstrap.Navbar
3853 * Bootstrap Sidebar class
3856 * Create a new Sidebar
3857 * @param {Object} config The config object
3861 Roo.bootstrap.NavSidebar = function(config){
3862 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3865 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3867 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3869 getAutoCreate : function(){
3874 cls: 'sidebar sidebar-nav'
3896 * @class Roo.bootstrap.NavGroup
3897 * @extends Roo.bootstrap.Component
3898 * Bootstrap NavGroup class
3899 * @cfg {String} align (left|right)
3900 * @cfg {Boolean} inverse
3901 * @cfg {String} type (nav|pills|tab) default nav
3902 * @cfg {String} navId - reference Id for navbar.
3906 * Create a new nav group
3907 * @param {Object} config The config object
3910 Roo.bootstrap.NavGroup = function(config){
3911 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3914 Roo.bootstrap.NavGroup.register(this);
3918 * Fires when the active item changes
3919 * @param {Roo.bootstrap.NavGroup} this
3920 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3921 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3928 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3939 getAutoCreate : function()
3941 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3948 if (['tabs','pills'].indexOf(this.type)!==-1) {
3949 cfg.cls += ' nav-' + this.type
3951 if (this.type!=='nav') {
3952 Roo.log('nav type must be nav/tabs/pills')
3954 cfg.cls += ' navbar-nav'
3957 if (this.parent().sidebar) {
3960 cls: 'dashboard-menu sidebar-menu'
3966 if (this.form === true) {
3972 if (this.align === 'right') {
3973 cfg.cls += ' navbar-right';
3975 cfg.cls += ' navbar-left';
3979 if (this.align === 'right') {
3980 cfg.cls += ' navbar-right';
3984 cfg.cls += ' navbar-inverse';
3992 * sets the active Navigation item
3993 * @param {Roo.bootstrap.NavItem} the new current navitem
3995 setActiveItem : function(item)
3998 Roo.each(this.navItems, function(v){
4003 v.setActive(false, true);
4010 item.setActive(true, true);
4011 this.fireEvent('changed', this, item, prev);
4016 * gets the active Navigation item
4017 * @return {Roo.bootstrap.NavItem} the current navitem
4019 getActive : function()
4023 Roo.each(this.navItems, function(v){
4034 indexOfNav : function()
4038 Roo.each(this.navItems, function(v,i){
4049 * adds a Navigation item
4050 * @param {Roo.bootstrap.NavItem} the navitem to add
4052 addItem : function(cfg)
4054 var cn = new Roo.bootstrap.NavItem(cfg);
4056 cn.parentId = this.id;
4057 cn.onRender(this.el, null);
4061 * register a Navigation item
4062 * @param {Roo.bootstrap.NavItem} the navitem to add
4064 register : function(item)
4066 this.navItems.push( item);
4067 item.navId = this.navId;
4072 * clear all the Navigation item
4075 clearAll : function()
4078 this.el.dom.innerHTML = '';
4081 getNavItem: function(tabId)
4084 Roo.each(this.navItems, function(e) {
4085 if (e.tabId == tabId) {
4095 setActiveNext : function()
4097 var i = this.indexOfNav(this.getActive());
4098 if (i > this.navItems.length) {
4101 this.setActiveItem(this.navItems[i+1]);
4103 setActivePrev : function()
4105 var i = this.indexOfNav(this.getActive());
4109 this.setActiveItem(this.navItems[i-1]);
4111 clearWasActive : function(except) {
4112 Roo.each(this.navItems, function(e) {
4113 if (e.tabId != except.tabId && e.was_active) {
4114 e.was_active = false;
4121 getWasActive : function ()
4124 Roo.each(this.navItems, function(e) {
4139 Roo.apply(Roo.bootstrap.NavGroup, {
4143 * register a Navigation Group
4144 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4146 register : function(navgrp)
4148 this.groups[navgrp.navId] = navgrp;
4152 * fetch a Navigation Group based on the navigation ID
4153 * @param {string} the navgroup to add
4154 * @returns {Roo.bootstrap.NavGroup} the navgroup
4156 get: function(navId) {
4157 if (typeof(this.groups[navId]) == 'undefined') {
4159 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4161 return this.groups[navId] ;
4176 * @class Roo.bootstrap.NavItem
4177 * @extends Roo.bootstrap.Component
4178 * Bootstrap Navbar.NavItem class
4179 * @cfg {String} href link to
4180 * @cfg {String} html content of button
4181 * @cfg {String} badge text inside badge
4182 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4183 * @cfg {String} glyphicon name of glyphicon
4184 * @cfg {String} icon name of font awesome icon
4185 * @cfg {Boolean} active Is item active
4186 * @cfg {Boolean} disabled Is item disabled
4188 * @cfg {Boolean} preventDefault (true | false) default false
4189 * @cfg {String} tabId the tab that this item activates.
4190 * @cfg {String} tagtype (a|span) render as a href or span?
4191 * @cfg {Boolean} animateRef (true|false) link to element default false
4194 * Create a new Navbar Item
4195 * @param {Object} config The config object
4197 Roo.bootstrap.NavItem = function(config){
4198 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4203 * The raw click event for the entire grid.
4204 * @param {Roo.EventObject} e
4209 * Fires when the active item active state changes
4210 * @param {Roo.bootstrap.NavItem} this
4211 * @param {boolean} state the new state
4217 * Fires when scroll to element
4218 * @param {Roo.bootstrap.NavItem} this
4219 * @param {Object} options
4220 * @param {Roo.EventObject} e
4228 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4236 preventDefault : false,
4243 getAutoCreate : function(){
4252 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4254 if (this.disabled) {
4255 cfg.cls += ' disabled';
4258 if (this.href || this.html || this.glyphicon || this.icon) {
4262 href : this.href || "#",
4263 html: this.html || ''
4268 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4271 if(this.glyphicon) {
4272 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4277 cfg.cn[0].html += " <span class='caret'></span>";
4281 if (this.badge !== '') {
4283 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4291 initEvents: function()
4293 if (typeof (this.menu) != 'undefined') {
4294 this.menu.parentType = this.xtype;
4295 this.menu.triggerEl = this.el;
4296 this.menu = this.addxtype(Roo.apply({}, this.menu));
4299 this.el.select('a',true).on('click', this.onClick, this);
4301 if(this.tagtype == 'span'){
4302 this.el.select('span',true).on('click', this.onClick, this);
4305 // at this point parent should be available..
4306 this.parent().register(this);
4309 onClick : function(e)
4311 if (e.getTarget('.dropdown-menu-item')) {
4312 // did you click on a menu itemm.... - then don't trigger onclick..
4317 this.preventDefault ||
4320 Roo.log("NavItem - prevent Default?");
4324 if (this.disabled) {
4328 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4329 if (tg && tg.transition) {
4330 Roo.log("waiting for the transitionend");
4336 //Roo.log("fire event clicked");
4337 if(this.fireEvent('click', this, e) === false){
4341 if(this.tagtype == 'span'){
4345 //Roo.log(this.href);
4346 var ael = this.el.select('a',true).first();
4349 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4350 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4351 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4352 return; // ignore... - it's a 'hash' to another page.
4354 Roo.log("NavItem - prevent Default?");
4356 this.scrollToElement(e);
4360 var p = this.parent();
4362 if (['tabs','pills'].indexOf(p.type)!==-1) {
4363 if (typeof(p.setActiveItem) !== 'undefined') {
4364 p.setActiveItem(this);
4368 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4369 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4370 // remove the collapsed menu expand...
4371 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4375 isActive: function () {
4378 setActive : function(state, fire, is_was_active)
4380 if (this.active && !state && this.navId) {
4381 this.was_active = true;
4382 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4384 nv.clearWasActive(this);
4388 this.active = state;
4391 this.el.removeClass('active');
4392 } else if (!this.el.hasClass('active')) {
4393 this.el.addClass('active');
4396 this.fireEvent('changed', this, state);
4399 // show a panel if it's registered and related..
4401 if (!this.navId || !this.tabId || !state || is_was_active) {
4405 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4409 var pan = tg.getPanelByName(this.tabId);
4413 // if we can not flip to new panel - go back to old nav highlight..
4414 if (false == tg.showPanel(pan)) {
4415 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4417 var onav = nv.getWasActive();
4419 onav.setActive(true, false, true);
4428 // this should not be here...
4429 setDisabled : function(state)
4431 this.disabled = state;
4433 this.el.removeClass('disabled');
4434 } else if (!this.el.hasClass('disabled')) {
4435 this.el.addClass('disabled');
4441 * Fetch the element to display the tooltip on.
4442 * @return {Roo.Element} defaults to this.el
4444 tooltipEl : function()
4446 return this.el.select('' + this.tagtype + '', true).first();
4449 scrollToElement : function(e)
4451 var c = document.body;
4454 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4456 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4457 c = document.documentElement;
4460 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4466 var o = target.calcOffsetsTo(c);
4473 this.fireEvent('scrollto', this, options, e);
4475 Roo.get(c).scrollTo('top', options.value, true);
4488 * <span> icon </span>
4489 * <span> text </span>
4490 * <span>badge </span>
4494 * @class Roo.bootstrap.NavSidebarItem
4495 * @extends Roo.bootstrap.NavItem
4496 * Bootstrap Navbar.NavSidebarItem class
4497 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4498 * {bool} open is the menu open
4500 * Create a new Navbar Button
4501 * @param {Object} config The config object
4503 Roo.bootstrap.NavSidebarItem = function(config){
4504 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4509 * The raw click event for the entire grid.
4510 * @param {Roo.EventObject} e
4515 * Fires when the active item active state changes
4516 * @param {Roo.bootstrap.NavSidebarItem} this
4517 * @param {boolean} state the new state
4525 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4527 badgeWeight : 'default',
4531 getAutoCreate : function(){
4536 href : this.href || '#',
4548 html : this.html || ''
4553 cfg.cls += ' active';
4556 if (this.disabled) {
4557 cfg.cls += ' disabled';
4560 cfg.cls += ' open x-open';
4563 if (this.glyphicon || this.icon) {
4564 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4565 a.cn.push({ tag : 'i', cls : c }) ;
4570 if (this.badge !== '') {
4572 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4576 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4577 a.cls += 'dropdown-toggle treeview' ;
4585 initEvents : function()
4587 if (typeof (this.menu) != 'undefined') {
4588 this.menu.parentType = this.xtype;
4589 this.menu.triggerEl = this.el;
4590 this.menu = this.addxtype(Roo.apply({}, this.menu));
4593 this.el.on('click', this.onClick, this);
4596 if(this.badge !== ''){
4598 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4603 onClick : function(e)
4610 if(this.preventDefault){
4614 this.fireEvent('click', this);
4617 disable : function()
4619 this.setDisabled(true);
4624 this.setDisabled(false);
4627 setDisabled : function(state)
4629 if(this.disabled == state){
4633 this.disabled = state;
4636 this.el.addClass('disabled');
4640 this.el.removeClass('disabled');
4645 setActive : function(state)
4647 if(this.active == state){
4651 this.active = state;
4654 this.el.addClass('active');
4658 this.el.removeClass('active');
4663 isActive: function ()
4668 setBadge : function(str)
4674 this.badgeEl.dom.innerHTML = str;
4691 * @class Roo.bootstrap.Row
4692 * @extends Roo.bootstrap.Component
4693 * Bootstrap Row class (contains columns...)
4697 * @param {Object} config The config object
4700 Roo.bootstrap.Row = function(config){
4701 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4704 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4706 getAutoCreate : function(){
4725 * @class Roo.bootstrap.Element
4726 * @extends Roo.bootstrap.Component
4727 * Bootstrap Element class
4728 * @cfg {String} html contents of the element
4729 * @cfg {String} tag tag of the element
4730 * @cfg {String} cls class of the element
4731 * @cfg {Boolean} preventDefault (true|false) default false
4732 * @cfg {Boolean} clickable (true|false) default false
4735 * Create a new Element
4736 * @param {Object} config The config object
4739 Roo.bootstrap.Element = function(config){
4740 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4746 * When a element is chick
4747 * @param {Roo.bootstrap.Element} this
4748 * @param {Roo.EventObject} e
4754 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4759 preventDefault: false,
4762 getAutoCreate : function(){
4773 initEvents: function()
4775 Roo.bootstrap.Element.superclass.initEvents.call(this);
4778 this.el.on('click', this.onClick, this);
4783 onClick : function(e)
4785 if(this.preventDefault){
4789 this.fireEvent('click', this, e);
4792 getValue : function()
4794 return this.el.dom.innerHTML;
4797 setValue : function(value)
4799 this.el.dom.innerHTML = value;
4814 * @class Roo.bootstrap.Pagination
4815 * @extends Roo.bootstrap.Component
4816 * Bootstrap Pagination class
4817 * @cfg {String} size xs | sm | md | lg
4818 * @cfg {Boolean} inverse false | true
4821 * Create a new Pagination
4822 * @param {Object} config The config object
4825 Roo.bootstrap.Pagination = function(config){
4826 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4829 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4835 getAutoCreate : function(){
4841 cfg.cls += ' inverse';
4847 cfg.cls += " " + this.cls;
4865 * @class Roo.bootstrap.PaginationItem
4866 * @extends Roo.bootstrap.Component
4867 * Bootstrap PaginationItem class
4868 * @cfg {String} html text
4869 * @cfg {String} href the link
4870 * @cfg {Boolean} preventDefault (true | false) default true
4871 * @cfg {Boolean} active (true | false) default false
4872 * @cfg {Boolean} disabled default false
4876 * Create a new PaginationItem
4877 * @param {Object} config The config object
4881 Roo.bootstrap.PaginationItem = function(config){
4882 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4887 * The raw click event for the entire grid.
4888 * @param {Roo.EventObject} e
4894 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4898 preventDefault: true,
4903 getAutoCreate : function(){
4909 href : this.href ? this.href : '#',
4910 html : this.html ? this.html : ''
4920 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4924 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4930 initEvents: function() {
4932 this.el.on('click', this.onClick, this);
4935 onClick : function(e)
4937 Roo.log('PaginationItem on click ');
4938 if(this.preventDefault){
4946 this.fireEvent('click', this, e);
4962 * @class Roo.bootstrap.Slider
4963 * @extends Roo.bootstrap.Component
4964 * Bootstrap Slider class
4967 * Create a new Slider
4968 * @param {Object} config The config object
4971 Roo.bootstrap.Slider = function(config){
4972 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4975 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4977 getAutoCreate : function(){
4981 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4985 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4997 * Ext JS Library 1.1.1
4998 * Copyright(c) 2006-2007, Ext JS, LLC.
5000 * Originally Released Under LGPL - original licence link has changed is not relivant.
5003 * <script type="text/javascript">
5008 * @class Roo.grid.ColumnModel
5009 * @extends Roo.util.Observable
5010 * This is the default implementation of a ColumnModel used by the Grid. It defines
5011 * the columns in the grid.
5014 var colModel = new Roo.grid.ColumnModel([
5015 {header: "Ticker", width: 60, sortable: true, locked: true},
5016 {header: "Company Name", width: 150, sortable: true},
5017 {header: "Market Cap.", width: 100, sortable: true},
5018 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5019 {header: "Employees", width: 100, sortable: true, resizable: false}
5024 * The config options listed for this class are options which may appear in each
5025 * individual column definition.
5026 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5028 * @param {Object} config An Array of column config objects. See this class's
5029 * config objects for details.
5031 Roo.grid.ColumnModel = function(config){
5033 * The config passed into the constructor
5035 this.config = config;
5038 // if no id, create one
5039 // if the column does not have a dataIndex mapping,
5040 // map it to the order it is in the config
5041 for(var i = 0, len = config.length; i < len; i++){
5043 if(typeof c.dataIndex == "undefined"){
5046 if(typeof c.renderer == "string"){
5047 c.renderer = Roo.util.Format[c.renderer];
5049 if(typeof c.id == "undefined"){
5052 if(c.editor && c.editor.xtype){
5053 c.editor = Roo.factory(c.editor, Roo.grid);
5055 if(c.editor && c.editor.isFormField){
5056 c.editor = new Roo.grid.GridEditor(c.editor);
5058 this.lookup[c.id] = c;
5062 * The width of columns which have no width specified (defaults to 100)
5065 this.defaultWidth = 100;
5068 * Default sortable of columns which have no sortable specified (defaults to false)
5071 this.defaultSortable = false;
5075 * @event widthchange
5076 * Fires when the width of a column changes.
5077 * @param {ColumnModel} this
5078 * @param {Number} columnIndex The column index
5079 * @param {Number} newWidth The new width
5081 "widthchange": true,
5083 * @event headerchange
5084 * Fires when the text of a header changes.
5085 * @param {ColumnModel} this
5086 * @param {Number} columnIndex The column index
5087 * @param {Number} newText The new header text
5089 "headerchange": true,
5091 * @event hiddenchange
5092 * Fires when a column is hidden or "unhidden".
5093 * @param {ColumnModel} this
5094 * @param {Number} columnIndex The column index
5095 * @param {Boolean} hidden true if hidden, false otherwise
5097 "hiddenchange": true,
5099 * @event columnmoved
5100 * Fires when a column is moved.
5101 * @param {ColumnModel} this
5102 * @param {Number} oldIndex
5103 * @param {Number} newIndex
5105 "columnmoved" : true,
5107 * @event columlockchange
5108 * Fires when a column's locked state is changed
5109 * @param {ColumnModel} this
5110 * @param {Number} colIndex
5111 * @param {Boolean} locked true if locked
5113 "columnlockchange" : true
5115 Roo.grid.ColumnModel.superclass.constructor.call(this);
5117 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5119 * @cfg {String} header The header text to display in the Grid view.
5122 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5123 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5124 * specified, the column's index is used as an index into the Record's data Array.
5127 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5128 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5131 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5132 * Defaults to the value of the {@link #defaultSortable} property.
5133 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5136 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5139 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5142 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5145 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5148 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5149 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5150 * default renderer uses the raw data value. If an object is returned (bootstrap only)
5151 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5154 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5157 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5160 * @cfg {String} cursor (Optional)
5163 * @cfg {String} tooltip (Optional)
5166 * @cfg {Number} xs (Optional)
5169 * @cfg {Number} sm (Optional)
5172 * @cfg {Number} md (Optional)
5175 * @cfg {Number} lg (Optional)
5178 * Returns the id of the column at the specified index.
5179 * @param {Number} index The column index
5180 * @return {String} the id
5182 getColumnId : function(index){
5183 return this.config[index].id;
5187 * Returns the column for a specified id.
5188 * @param {String} id The column id
5189 * @return {Object} the column
5191 getColumnById : function(id){
5192 return this.lookup[id];
5197 * Returns the column for a specified dataIndex.
5198 * @param {String} dataIndex The column dataIndex
5199 * @return {Object|Boolean} the column or false if not found
5201 getColumnByDataIndex: function(dataIndex){
5202 var index = this.findColumnIndex(dataIndex);
5203 return index > -1 ? this.config[index] : false;
5207 * Returns the index for a specified column id.
5208 * @param {String} id The column id
5209 * @return {Number} the index, or -1 if not found
5211 getIndexById : function(id){
5212 for(var i = 0, len = this.config.length; i < len; i++){
5213 if(this.config[i].id == id){
5221 * Returns the index for a specified column dataIndex.
5222 * @param {String} dataIndex The column dataIndex
5223 * @return {Number} the index, or -1 if not found
5226 findColumnIndex : function(dataIndex){
5227 for(var i = 0, len = this.config.length; i < len; i++){
5228 if(this.config[i].dataIndex == dataIndex){
5236 moveColumn : function(oldIndex, newIndex){
5237 var c = this.config[oldIndex];
5238 this.config.splice(oldIndex, 1);
5239 this.config.splice(newIndex, 0, c);
5240 this.dataMap = null;
5241 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5244 isLocked : function(colIndex){
5245 return this.config[colIndex].locked === true;
5248 setLocked : function(colIndex, value, suppressEvent){
5249 if(this.isLocked(colIndex) == value){
5252 this.config[colIndex].locked = value;
5254 this.fireEvent("columnlockchange", this, colIndex, value);
5258 getTotalLockedWidth : function(){
5260 for(var i = 0; i < this.config.length; i++){
5261 if(this.isLocked(i) && !this.isHidden(i)){
5262 this.totalWidth += this.getColumnWidth(i);
5268 getLockedCount : function(){
5269 for(var i = 0, len = this.config.length; i < len; i++){
5270 if(!this.isLocked(i)){
5275 return this.config.length;
5279 * Returns the number of columns.
5282 getColumnCount : function(visibleOnly){
5283 if(visibleOnly === true){
5285 for(var i = 0, len = this.config.length; i < len; i++){
5286 if(!this.isHidden(i)){
5292 return this.config.length;
5296 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5297 * @param {Function} fn
5298 * @param {Object} scope (optional)
5299 * @return {Array} result
5301 getColumnsBy : function(fn, scope){
5303 for(var i = 0, len = this.config.length; i < len; i++){
5304 var c = this.config[i];
5305 if(fn.call(scope||this, c, i) === true){
5313 * Returns true if the specified column is sortable.
5314 * @param {Number} col The column index
5317 isSortable : function(col){
5318 if(typeof this.config[col].sortable == "undefined"){
5319 return this.defaultSortable;
5321 return this.config[col].sortable;
5325 * Returns the rendering (formatting) function defined for the column.
5326 * @param {Number} col The column index.
5327 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5329 getRenderer : function(col){
5330 if(!this.config[col].renderer){
5331 return Roo.grid.ColumnModel.defaultRenderer;
5333 return this.config[col].renderer;
5337 * Sets the rendering (formatting) function for a column.
5338 * @param {Number} col The column index
5339 * @param {Function} fn The function to use to process the cell's raw data
5340 * to return HTML markup for the grid view. The render function is called with
5341 * the following parameters:<ul>
5342 * <li>Data value.</li>
5343 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5344 * <li>css A CSS style string to apply to the table cell.</li>
5345 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5346 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5347 * <li>Row index</li>
5348 * <li>Column index</li>
5349 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5351 setRenderer : function(col, fn){
5352 this.config[col].renderer = fn;
5356 * Returns the width for the specified column.
5357 * @param {Number} col The column index
5360 getColumnWidth : function(col){
5361 return this.config[col].width * 1 || this.defaultWidth;
5365 * Sets the width for a column.
5366 * @param {Number} col The column index
5367 * @param {Number} width The new width
5369 setColumnWidth : function(col, width, suppressEvent){
5370 this.config[col].width = width;
5371 this.totalWidth = null;
5373 this.fireEvent("widthchange", this, col, width);
5378 * Returns the total width of all columns.
5379 * @param {Boolean} includeHidden True to include hidden column widths
5382 getTotalWidth : function(includeHidden){
5383 if(!this.totalWidth){
5384 this.totalWidth = 0;
5385 for(var i = 0, len = this.config.length; i < len; i++){
5386 if(includeHidden || !this.isHidden(i)){
5387 this.totalWidth += this.getColumnWidth(i);
5391 return this.totalWidth;
5395 * Returns the header for the specified column.
5396 * @param {Number} col The column index
5399 getColumnHeader : function(col){
5400 return this.config[col].header;
5404 * Sets the header for a column.
5405 * @param {Number} col The column index
5406 * @param {String} header The new header
5408 setColumnHeader : function(col, header){
5409 this.config[col].header = header;
5410 this.fireEvent("headerchange", this, col, header);
5414 * Returns the tooltip for the specified column.
5415 * @param {Number} col The column index
5418 getColumnTooltip : function(col){
5419 return this.config[col].tooltip;
5422 * Sets the tooltip for a column.
5423 * @param {Number} col The column index
5424 * @param {String} tooltip The new tooltip
5426 setColumnTooltip : function(col, tooltip){
5427 this.config[col].tooltip = tooltip;
5431 * Returns the dataIndex for the specified column.
5432 * @param {Number} col The column index
5435 getDataIndex : function(col){
5436 return this.config[col].dataIndex;
5440 * Sets the dataIndex for a column.
5441 * @param {Number} col The column index
5442 * @param {Number} dataIndex The new dataIndex
5444 setDataIndex : function(col, dataIndex){
5445 this.config[col].dataIndex = dataIndex;
5451 * Returns true if the cell is editable.
5452 * @param {Number} colIndex The column index
5453 * @param {Number} rowIndex The row index - this is nto actually used..?
5456 isCellEditable : function(colIndex, rowIndex){
5457 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5461 * Returns the editor defined for the cell/column.
5462 * return false or null to disable editing.
5463 * @param {Number} colIndex The column index
5464 * @param {Number} rowIndex The row index
5467 getCellEditor : function(colIndex, rowIndex){
5468 return this.config[colIndex].editor;
5472 * Sets if a column is editable.
5473 * @param {Number} col The column index
5474 * @param {Boolean} editable True if the column is editable
5476 setEditable : function(col, editable){
5477 this.config[col].editable = editable;
5482 * Returns true if the column is hidden.
5483 * @param {Number} colIndex The column index
5486 isHidden : function(colIndex){
5487 return this.config[colIndex].hidden;
5492 * Returns true if the column width cannot be changed
5494 isFixed : function(colIndex){
5495 return this.config[colIndex].fixed;
5499 * Returns true if the column can be resized
5502 isResizable : function(colIndex){
5503 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5506 * Sets if a column is hidden.
5507 * @param {Number} colIndex The column index
5508 * @param {Boolean} hidden True if the column is hidden
5510 setHidden : function(colIndex, hidden){
5511 this.config[colIndex].hidden = hidden;
5512 this.totalWidth = null;
5513 this.fireEvent("hiddenchange", this, colIndex, hidden);
5517 * Sets the editor for a column.
5518 * @param {Number} col The column index
5519 * @param {Object} editor The editor object
5521 setEditor : function(col, editor){
5522 this.config[col].editor = editor;
5526 Roo.grid.ColumnModel.defaultRenderer = function(value){
5527 if(typeof value == "string" && value.length < 1){
5533 // Alias for backwards compatibility
5534 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5537 * Ext JS Library 1.1.1
5538 * Copyright(c) 2006-2007, Ext JS, LLC.
5540 * Originally Released Under LGPL - original licence link has changed is not relivant.
5543 * <script type="text/javascript">
5547 * @class Roo.LoadMask
5548 * A simple utility class for generically masking elements while loading data. If the element being masked has
5549 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5550 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5551 * element's UpdateManager load indicator and will be destroyed after the initial load.
5553 * Create a new LoadMask
5554 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5555 * @param {Object} config The config object
5557 Roo.LoadMask = function(el, config){
5558 this.el = Roo.get(el);
5559 Roo.apply(this, config);
5561 this.store.on('beforeload', this.onBeforeLoad, this);
5562 this.store.on('load', this.onLoad, this);
5563 this.store.on('loadexception', this.onLoadException, this);
5564 this.removeMask = false;
5566 var um = this.el.getUpdateManager();
5567 um.showLoadIndicator = false; // disable the default indicator
5568 um.on('beforeupdate', this.onBeforeLoad, this);
5569 um.on('update', this.onLoad, this);
5570 um.on('failure', this.onLoad, this);
5571 this.removeMask = true;
5575 Roo.LoadMask.prototype = {
5577 * @cfg {Boolean} removeMask
5578 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5579 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5583 * The text to display in a centered loading message box (defaults to 'Loading...')
5587 * @cfg {String} msgCls
5588 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5590 msgCls : 'x-mask-loading',
5593 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5599 * Disables the mask to prevent it from being displayed
5601 disable : function(){
5602 this.disabled = true;
5606 * Enables the mask so that it can be displayed
5608 enable : function(){
5609 this.disabled = false;
5612 onLoadException : function()
5616 if (typeof(arguments[3]) != 'undefined') {
5617 Roo.MessageBox.alert("Error loading",arguments[3]);
5621 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5622 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5631 this.el.unmask(this.removeMask);
5636 this.el.unmask(this.removeMask);
5640 onBeforeLoad : function(){
5642 this.el.mask(this.msg, this.msgCls);
5647 destroy : function(){
5649 this.store.un('beforeload', this.onBeforeLoad, this);
5650 this.store.un('load', this.onLoad, this);
5651 this.store.un('loadexception', this.onLoadException, this);
5653 var um = this.el.getUpdateManager();
5654 um.un('beforeupdate', this.onBeforeLoad, this);
5655 um.un('update', this.onLoad, this);
5656 um.un('failure', this.onLoad, this);
5667 * @class Roo.bootstrap.Table
5668 * @extends Roo.bootstrap.Component
5669 * Bootstrap Table class
5670 * @cfg {String} cls table class
5671 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5672 * @cfg {String} bgcolor Specifies the background color for a table
5673 * @cfg {Number} border Specifies whether the table cells should have borders or not
5674 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5675 * @cfg {Number} cellspacing Specifies the space between cells
5676 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5677 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5678 * @cfg {String} sortable Specifies that the table should be sortable
5679 * @cfg {String} summary Specifies a summary of the content of a table
5680 * @cfg {Number} width Specifies the width of a table
5681 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5683 * @cfg {boolean} striped Should the rows be alternative striped
5684 * @cfg {boolean} bordered Add borders to the table
5685 * @cfg {boolean} hover Add hover highlighting
5686 * @cfg {boolean} condensed Format condensed
5687 * @cfg {boolean} responsive Format condensed
5688 * @cfg {Boolean} loadMask (true|false) default false
5689 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5690 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5691 * @cfg {Boolean} rowSelection (true|false) default false
5692 * @cfg {Boolean} cellSelection (true|false) default false
5693 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5694 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5698 * Create a new Table
5699 * @param {Object} config The config object
5702 Roo.bootstrap.Table = function(config){
5703 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5708 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5709 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5710 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5711 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5715 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5716 this.sm = this.selModel;
5717 this.sm.xmodule = this.xmodule || false;
5719 if (this.cm && typeof(this.cm.config) == 'undefined') {
5720 this.colModel = new Roo.grid.ColumnModel(this.cm);
5721 this.cm = this.colModel;
5722 this.cm.xmodule = this.xmodule || false;
5725 this.store= Roo.factory(this.store, Roo.data);
5726 this.ds = this.store;
5727 this.ds.xmodule = this.xmodule || false;
5730 if (this.footer && this.store) {
5731 this.footer.dataSource = this.ds;
5732 this.footer = Roo.factory(this.footer);
5739 * Fires when a cell is clicked
5740 * @param {Roo.bootstrap.Table} this
5741 * @param {Roo.Element} el
5742 * @param {Number} rowIndex
5743 * @param {Number} columnIndex
5744 * @param {Roo.EventObject} e
5748 * @event celldblclick
5749 * Fires when a cell is double clicked
5750 * @param {Roo.bootstrap.Table} this
5751 * @param {Roo.Element} el
5752 * @param {Number} rowIndex
5753 * @param {Number} columnIndex
5754 * @param {Roo.EventObject} e
5756 "celldblclick" : true,
5759 * Fires when a row is clicked
5760 * @param {Roo.bootstrap.Table} this
5761 * @param {Roo.Element} el
5762 * @param {Number} rowIndex
5763 * @param {Roo.EventObject} e
5767 * @event rowdblclick
5768 * Fires when a row is double clicked
5769 * @param {Roo.bootstrap.Table} this
5770 * @param {Roo.Element} el
5771 * @param {Number} rowIndex
5772 * @param {Roo.EventObject} e
5774 "rowdblclick" : true,
5777 * Fires when a mouseover occur
5778 * @param {Roo.bootstrap.Table} this
5779 * @param {Roo.Element} el
5780 * @param {Number} rowIndex
5781 * @param {Number} columnIndex
5782 * @param {Roo.EventObject} e
5787 * Fires when a mouseout occur
5788 * @param {Roo.bootstrap.Table} this
5789 * @param {Roo.Element} el
5790 * @param {Number} rowIndex
5791 * @param {Number} columnIndex
5792 * @param {Roo.EventObject} e
5797 * Fires when a row is rendered, so you can change add a style to it.
5798 * @param {Roo.bootstrap.Table} this
5799 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5803 * @event rowsrendered
5804 * Fires when all the rows have been rendered
5805 * @param {Roo.bootstrap.Table} this
5807 'rowsrendered' : true,
5809 * @event contextmenu
5810 * The raw contextmenu event for the entire grid.
5811 * @param {Roo.EventObject} e
5813 "contextmenu" : true,
5815 * @event rowcontextmenu
5816 * Fires when a row is right clicked
5817 * @param {Roo.bootstrap.Table} this
5818 * @param {Number} rowIndex
5819 * @param {Roo.EventObject} e
5821 "rowcontextmenu" : true,
5823 * @event cellcontextmenu
5824 * Fires when a cell is right clicked
5825 * @param {Roo.bootstrap.Table} this
5826 * @param {Number} rowIndex
5827 * @param {Number} cellIndex
5828 * @param {Roo.EventObject} e
5830 "cellcontextmenu" : true,
5832 * @event headercontextmenu
5833 * Fires when a header is right clicked
5834 * @param {Roo.bootstrap.Table} this
5835 * @param {Number} columnIndex
5836 * @param {Roo.EventObject} e
5838 "headercontextmenu" : true
5842 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5868 rowSelection : false,
5869 cellSelection : false,
5872 // Roo.Element - the tbody
5874 // Roo.Element - thead element
5877 container: false, // used by gridpanel...
5879 getAutoCreate : function()
5881 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5888 if (this.scrollBody) {
5889 cfg.cls += ' table-body-fixed';
5892 cfg.cls += ' table-striped';
5896 cfg.cls += ' table-hover';
5898 if (this.bordered) {
5899 cfg.cls += ' table-bordered';
5901 if (this.condensed) {
5902 cfg.cls += ' table-condensed';
5904 if (this.responsive) {
5905 cfg.cls += ' table-responsive';
5909 cfg.cls+= ' ' +this.cls;
5912 // this lot should be simplifed...
5915 cfg.align=this.align;
5918 cfg.bgcolor=this.bgcolor;
5921 cfg.border=this.border;
5923 if (this.cellpadding) {
5924 cfg.cellpadding=this.cellpadding;
5926 if (this.cellspacing) {
5927 cfg.cellspacing=this.cellspacing;
5930 cfg.frame=this.frame;
5933 cfg.rules=this.rules;
5935 if (this.sortable) {
5936 cfg.sortable=this.sortable;
5939 cfg.summary=this.summary;
5942 cfg.width=this.width;
5945 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5948 if(this.store || this.cm){
5949 if(this.headerShow){
5950 cfg.cn.push(this.renderHeader());
5953 cfg.cn.push(this.renderBody());
5955 if(this.footerShow){
5956 cfg.cn.push(this.renderFooter());
5958 // where does this come from?
5959 //cfg.cls+= ' TableGrid';
5962 return { cn : [ cfg ] };
5965 initEvents : function()
5967 if(!this.store || !this.cm){
5971 //Roo.log('initEvents with ds!!!!');
5973 this.mainBody = this.el.select('tbody', true).first();
5974 this.mainHead = this.el.select('thead', true).first();
5980 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5981 e.on('click', _this.sort, _this);
5984 this.el.on("click", this.onClick, this);
5985 this.el.on("dblclick", this.onDblClick, this);
5987 // why is this done????? = it breaks dialogs??
5988 //this.parent().el.setStyle('position', 'relative');
5992 this.footer.parentId = this.id;
5993 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5996 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5998 this.store.on('load', this.onLoad, this);
5999 this.store.on('beforeload', this.onBeforeLoad, this);
6000 this.store.on('update', this.onUpdate, this);
6001 this.store.on('add', this.onAdd, this);
6003 this.el.on("contextmenu", this.onContextMenu, this);
6005 this.mainBody.on('scroll', this.onBodyScroll, this);
6010 onContextMenu : function(e, t)
6012 this.processEvent("contextmenu", e);
6015 processEvent : function(name, e)
6017 if (name != 'touchstart' ) {
6018 this.fireEvent(name, e);
6021 var t = e.getTarget();
6023 var cell = Roo.get(t);
6029 if(cell.findParent('tfoot', false, true)){
6033 if(cell.findParent('thead', false, true)){
6035 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6036 cell = Roo.get(t).findParent('th', false, true);
6038 Roo.log("failed to find th in thead?");
6039 Roo.log(e.getTarget());
6044 var cellIndex = cell.dom.cellIndex;
6046 var ename = name == 'touchstart' ? 'click' : name;
6047 this.fireEvent("header" + ename, this, cellIndex, e);
6052 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6053 cell = Roo.get(t).findParent('td', false, true);
6055 Roo.log("failed to find th in tbody?");
6056 Roo.log(e.getTarget());
6061 var row = cell.findParent('tr', false, true);
6062 var cellIndex = cell.dom.cellIndex;
6063 var rowIndex = row.dom.rowIndex - 1;
6067 this.fireEvent("row" + name, this, rowIndex, e);
6071 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6077 onMouseover : function(e, el)
6079 var cell = Roo.get(el);
6085 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6086 cell = cell.findParent('td', false, true);
6089 var row = cell.findParent('tr', false, true);
6090 var cellIndex = cell.dom.cellIndex;
6091 var rowIndex = row.dom.rowIndex - 1; // start from 0
6093 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6097 onMouseout : function(e, el)
6099 var cell = Roo.get(el);
6105 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6106 cell = cell.findParent('td', false, true);
6109 var row = cell.findParent('tr', false, true);
6110 var cellIndex = cell.dom.cellIndex;
6111 var rowIndex = row.dom.rowIndex - 1; // start from 0
6113 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6117 onClick : function(e, el)
6119 var cell = Roo.get(el);
6121 if(!cell || (!this.cellSelection && !this.rowSelection)){
6125 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6126 cell = cell.findParent('td', false, true);
6129 if(!cell || typeof(cell) == 'undefined'){
6133 var row = cell.findParent('tr', false, true);
6135 if(!row || typeof(row) == 'undefined'){
6139 var cellIndex = cell.dom.cellIndex;
6140 var rowIndex = this.getRowIndex(row);
6142 // why??? - should these not be based on SelectionModel?
6143 if(this.cellSelection){
6144 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6147 if(this.rowSelection){
6148 this.fireEvent('rowclick', this, row, rowIndex, e);
6154 onDblClick : function(e,el)
6156 var cell = Roo.get(el);
6158 if(!cell || (!this.CellSelection && !this.RowSelection)){
6162 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6163 cell = cell.findParent('td', false, true);
6166 if(!cell || typeof(cell) == 'undefined'){
6170 var row = cell.findParent('tr', false, true);
6172 if(!row || typeof(row) == 'undefined'){
6176 var cellIndex = cell.dom.cellIndex;
6177 var rowIndex = this.getRowIndex(row);
6179 if(this.CellSelection){
6180 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6183 if(this.RowSelection){
6184 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6188 sort : function(e,el)
6190 var col = Roo.get(el);
6192 if(!col.hasClass('sortable')){
6196 var sort = col.attr('sort');
6199 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6203 this.store.sortInfo = {field : sort, direction : dir};
6206 Roo.log("calling footer first");
6207 this.footer.onClick('first');
6210 this.store.load({ params : { start : 0 } });
6214 renderHeader : function()
6222 this.totalWidth = 0;
6224 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6226 var config = cm.config[i];
6231 html: cm.getColumnHeader(i)
6236 if(typeof(config.sortable) != 'undefined' && config.sortable){
6238 c.html = '<i class="glyphicon"></i>' + c.html;
6241 if(typeof(config.lgHeader) != 'undefined'){
6242 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6245 if(typeof(config.mdHeader) != 'undefined'){
6246 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6249 if(typeof(config.smHeader) != 'undefined'){
6250 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6253 if(typeof(config.xsHeader) != 'undefined'){
6254 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6261 if(typeof(config.tooltip) != 'undefined'){
6262 c.tooltip = config.tooltip;
6265 if(typeof(config.colspan) != 'undefined'){
6266 c.colspan = config.colspan;
6269 if(typeof(config.hidden) != 'undefined' && config.hidden){
6270 c.style += ' display:none;';
6273 if(typeof(config.dataIndex) != 'undefined'){
6274 c.sort = config.dataIndex;
6279 if(typeof(config.align) != 'undefined' && config.align.length){
6280 c.style += ' text-align:' + config.align + ';';
6283 if(typeof(config.width) != 'undefined'){
6284 c.style += ' width:' + config.width + 'px;';
6285 this.totalWidth += config.width;
6287 this.totalWidth += 100; // assume minimum of 100 per column?
6290 if(typeof(config.cls) != 'undefined'){
6291 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6294 ['xs','sm','md','lg'].map(function(size){
6296 if(typeof(config[size]) == 'undefined'){
6300 if (!config[size]) { // 0 = hidden
6301 c.cls += ' hidden-' + size;
6305 c.cls += ' col-' + size + '-' + config[size];
6315 renderBody : function()
6325 colspan : this.cm.getColumnCount()
6335 renderFooter : function()
6345 colspan : this.cm.getColumnCount()
6359 // Roo.log('ds onload');
6364 var ds = this.store;
6366 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6367 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6368 if (_this.store.sortInfo) {
6370 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6371 e.select('i', true).addClass(['glyphicon-arrow-up']);
6374 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6375 e.select('i', true).addClass(['glyphicon-arrow-down']);
6380 var tbody = this.mainBody;
6382 if(ds.getCount() > 0){
6383 ds.data.each(function(d,rowIndex){
6384 var row = this.renderRow(cm, ds, rowIndex);
6386 tbody.createChild(row);
6390 if(row.cellObjects.length){
6391 Roo.each(row.cellObjects, function(r){
6392 _this.renderCellObject(r);
6399 Roo.each(this.el.select('tbody td', true).elements, function(e){
6400 e.on('mouseover', _this.onMouseover, _this);
6403 Roo.each(this.el.select('tbody td', true).elements, function(e){
6404 e.on('mouseout', _this.onMouseout, _this);
6406 this.fireEvent('rowsrendered', this);
6407 //if(this.loadMask){
6408 // this.maskEl.hide();
6415 onUpdate : function(ds,record)
6417 this.refreshRow(record);
6420 onRemove : function(ds, record, index, isUpdate){
6421 if(isUpdate !== true){
6422 this.fireEvent("beforerowremoved", this, index, record);
6424 var bt = this.mainBody.dom;
6426 var rows = this.el.select('tbody > tr', true).elements;
6428 if(typeof(rows[index]) != 'undefined'){
6429 bt.removeChild(rows[index].dom);
6432 // if(bt.rows[index]){
6433 // bt.removeChild(bt.rows[index]);
6436 if(isUpdate !== true){
6437 //this.stripeRows(index);
6438 //this.syncRowHeights(index, index);
6440 this.fireEvent("rowremoved", this, index, record);
6444 onAdd : function(ds, records, rowIndex)
6446 //Roo.log('on Add called');
6447 // - note this does not handle multiple adding very well..
6448 var bt = this.mainBody.dom;
6449 for (var i =0 ; i < records.length;i++) {
6450 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6451 //Roo.log(records[i]);
6452 //Roo.log(this.store.getAt(rowIndex+i));
6453 this.insertRow(this.store, rowIndex + i, false);
6460 refreshRow : function(record){
6461 var ds = this.store, index;
6462 if(typeof record == 'number'){
6464 record = ds.getAt(index);
6466 index = ds.indexOf(record);
6468 this.insertRow(ds, index, true);
6469 this.onRemove(ds, record, index+1, true);
6470 //this.syncRowHeights(index, index);
6472 this.fireEvent("rowupdated", this, index, record);
6475 insertRow : function(dm, rowIndex, isUpdate){
6478 this.fireEvent("beforerowsinserted", this, rowIndex);
6480 //var s = this.getScrollState();
6481 var row = this.renderRow(this.cm, this.store, rowIndex);
6482 // insert before rowIndex..
6483 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6487 if(row.cellObjects.length){
6488 Roo.each(row.cellObjects, function(r){
6489 _this.renderCellObject(r);
6494 this.fireEvent("rowsinserted", this, rowIndex);
6495 //this.syncRowHeights(firstRow, lastRow);
6496 //this.stripeRows(firstRow);
6503 getRowDom : function(rowIndex)
6505 var rows = this.el.select('tbody > tr', true).elements;
6507 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6510 // returns the object tree for a tr..
6513 renderRow : function(cm, ds, rowIndex)
6516 var d = ds.getAt(rowIndex);
6523 var cellObjects = [];
6525 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6526 var config = cm.config[i];
6528 var renderer = cm.getRenderer(i);
6532 if(typeof(renderer) !== 'undefined'){
6533 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6535 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6536 // and are rendered into the cells after the row is rendered - using the id for the element.
6538 if(typeof(value) === 'object'){
6548 rowIndex : rowIndex,
6553 this.fireEvent('rowclass', this, rowcfg);
6557 cls : rowcfg.rowClass,
6559 html: (typeof(value) === 'object') ? '' : value
6566 if(typeof(config.colspan) != 'undefined'){
6567 td.colspan = config.colspan;
6570 if(typeof(config.hidden) != 'undefined' && config.hidden){
6571 td.style += ' display:none;';
6574 if(typeof(config.align) != 'undefined' && config.align.length){
6575 td.style += ' text-align:' + config.align + ';';
6578 if(typeof(config.width) != 'undefined'){
6579 td.style += ' width:' + config.width + 'px;';
6582 if(typeof(config.cursor) != 'undefined'){
6583 td.style += ' cursor:' + config.cursor + ';';
6586 if(typeof(config.cls) != 'undefined'){
6587 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6590 ['xs','sm','md','lg'].map(function(size){
6592 if(typeof(config[size]) == 'undefined'){
6596 if (!config[size]) { // 0 = hidden
6597 td.cls += ' hidden-' + size;
6601 td.cls += ' col-' + size + '-' + config[size];
6609 row.cellObjects = cellObjects;
6617 onBeforeLoad : function()
6619 //Roo.log('ds onBeforeLoad');
6623 //if(this.loadMask){
6624 // this.maskEl.show();
6632 this.el.select('tbody', true).first().dom.innerHTML = '';
6635 * Show or hide a row.
6636 * @param {Number} rowIndex to show or hide
6637 * @param {Boolean} state hide
6639 setRowVisibility : function(rowIndex, state)
6641 var bt = this.mainBody.dom;
6643 var rows = this.el.select('tbody > tr', true).elements;
6645 if(typeof(rows[rowIndex]) == 'undefined'){
6648 rows[rowIndex].dom.style.display = state ? '' : 'none';
6652 getSelectionModel : function(){
6654 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6656 return this.selModel;
6659 * Render the Roo.bootstrap object from renderder
6661 renderCellObject : function(r)
6665 var t = r.cfg.render(r.container);
6668 Roo.each(r.cfg.cn, function(c){
6670 container: t.getChildContainer(),
6673 _this.renderCellObject(child);
6678 getRowIndex : function(row)
6682 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6693 * Returns the grid's underlying element = used by panel.Grid
6694 * @return {Element} The element
6696 getGridEl : function(){
6700 * Forces a resize - used by panel.Grid
6701 * @return {Element} The element
6703 autoSize : function(){
6704 //var ctr = Roo.get(this.container.dom.parentElement);
6705 var ctr = Roo.get(this.el.dom);
6707 var thd = this.getGridEl().select('thead',true).first();
6708 var tbd = this.getGridEl().select('tbody', true).first();
6711 var cw = ctr.getWidth();
6715 tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6716 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6719 cw = Math.max(cw, this.totalWidth);
6720 this.getGridEl().select('tr',true).setWidth(cw);
6722 return; // we doe not have a view in this design..
6725 onBodyScroll: function()
6728 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6729 this.mainHead.setStyle({
6730 'position' : 'relative',
6731 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6748 * @class Roo.bootstrap.TableCell
6749 * @extends Roo.bootstrap.Component
6750 * Bootstrap TableCell class
6751 * @cfg {String} html cell contain text
6752 * @cfg {String} cls cell class
6753 * @cfg {String} tag cell tag (td|th) default td
6754 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6755 * @cfg {String} align Aligns the content in a cell
6756 * @cfg {String} axis Categorizes cells
6757 * @cfg {String} bgcolor Specifies the background color of a cell
6758 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6759 * @cfg {Number} colspan Specifies the number of columns a cell should span
6760 * @cfg {String} headers Specifies one or more header cells a cell is related to
6761 * @cfg {Number} height Sets the height of a cell
6762 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6763 * @cfg {Number} rowspan Sets the number of rows a cell should span
6764 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6765 * @cfg {String} valign Vertical aligns the content in a cell
6766 * @cfg {Number} width Specifies the width of a cell
6769 * Create a new TableCell
6770 * @param {Object} config The config object
6773 Roo.bootstrap.TableCell = function(config){
6774 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6777 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6797 getAutoCreate : function(){
6798 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6818 cfg.align=this.align
6824 cfg.bgcolor=this.bgcolor
6827 cfg.charoff=this.charoff
6830 cfg.colspan=this.colspan
6833 cfg.headers=this.headers
6836 cfg.height=this.height
6839 cfg.nowrap=this.nowrap
6842 cfg.rowspan=this.rowspan
6845 cfg.scope=this.scope
6848 cfg.valign=this.valign
6851 cfg.width=this.width
6870 * @class Roo.bootstrap.TableRow
6871 * @extends Roo.bootstrap.Component
6872 * Bootstrap TableRow class
6873 * @cfg {String} cls row class
6874 * @cfg {String} align Aligns the content in a table row
6875 * @cfg {String} bgcolor Specifies a background color for a table row
6876 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6877 * @cfg {String} valign Vertical aligns the content in a table row
6880 * Create a new TableRow
6881 * @param {Object} config The config object
6884 Roo.bootstrap.TableRow = function(config){
6885 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6888 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6896 getAutoCreate : function(){
6897 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6907 cfg.align = this.align;
6910 cfg.bgcolor = this.bgcolor;
6913 cfg.charoff = this.charoff;
6916 cfg.valign = this.valign;
6934 * @class Roo.bootstrap.TableBody
6935 * @extends Roo.bootstrap.Component
6936 * Bootstrap TableBody class
6937 * @cfg {String} cls element class
6938 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6939 * @cfg {String} align Aligns the content inside the element
6940 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6941 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6944 * Create a new TableBody
6945 * @param {Object} config The config object
6948 Roo.bootstrap.TableBody = function(config){
6949 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6952 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6960 getAutoCreate : function(){
6961 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6975 cfg.align = this.align;
6978 cfg.charoff = this.charoff;
6981 cfg.valign = this.valign;
6988 // initEvents : function()
6995 // this.store = Roo.factory(this.store, Roo.data);
6996 // this.store.on('load', this.onLoad, this);
6998 // this.store.load();
7002 // onLoad: function ()
7004 // this.fireEvent('load', this);
7014 * Ext JS Library 1.1.1
7015 * Copyright(c) 2006-2007, Ext JS, LLC.
7017 * Originally Released Under LGPL - original licence link has changed is not relivant.
7020 * <script type="text/javascript">
7023 // as we use this in bootstrap.
7024 Roo.namespace('Roo.form');
7026 * @class Roo.form.Action
7027 * Internal Class used to handle form actions
7029 * @param {Roo.form.BasicForm} el The form element or its id
7030 * @param {Object} config Configuration options
7035 // define the action interface
7036 Roo.form.Action = function(form, options){
7038 this.options = options || {};
7041 * Client Validation Failed
7044 Roo.form.Action.CLIENT_INVALID = 'client';
7046 * Server Validation Failed
7049 Roo.form.Action.SERVER_INVALID = 'server';
7051 * Connect to Server Failed
7054 Roo.form.Action.CONNECT_FAILURE = 'connect';
7056 * Reading Data from Server Failed
7059 Roo.form.Action.LOAD_FAILURE = 'load';
7061 Roo.form.Action.prototype = {
7063 failureType : undefined,
7064 response : undefined,
7068 run : function(options){
7073 success : function(response){
7078 handleResponse : function(response){
7082 // default connection failure
7083 failure : function(response){
7085 this.response = response;
7086 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7087 this.form.afterAction(this, false);
7090 processResponse : function(response){
7091 this.response = response;
7092 if(!response.responseText){
7095 this.result = this.handleResponse(response);
7099 // utility functions used internally
7100 getUrl : function(appendParams){
7101 var url = this.options.url || this.form.url || this.form.el.dom.action;
7103 var p = this.getParams();
7105 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7111 getMethod : function(){
7112 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7115 getParams : function(){
7116 var bp = this.form.baseParams;
7117 var p = this.options.params;
7119 if(typeof p == "object"){
7120 p = Roo.urlEncode(Roo.applyIf(p, bp));
7121 }else if(typeof p == 'string' && bp){
7122 p += '&' + Roo.urlEncode(bp);
7125 p = Roo.urlEncode(bp);
7130 createCallback : function(){
7132 success: this.success,
7133 failure: this.failure,
7135 timeout: (this.form.timeout*1000),
7136 upload: this.form.fileUpload ? this.success : undefined
7141 Roo.form.Action.Submit = function(form, options){
7142 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7145 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7148 haveProgress : false,
7149 uploadComplete : false,
7151 // uploadProgress indicator.
7152 uploadProgress : function()
7154 if (!this.form.progressUrl) {
7158 if (!this.haveProgress) {
7159 Roo.MessageBox.progress("Uploading", "Uploading");
7161 if (this.uploadComplete) {
7162 Roo.MessageBox.hide();
7166 this.haveProgress = true;
7168 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7170 var c = new Roo.data.Connection();
7172 url : this.form.progressUrl,
7177 success : function(req){
7178 //console.log(data);
7182 rdata = Roo.decode(req.responseText)
7184 Roo.log("Invalid data from server..");
7188 if (!rdata || !rdata.success) {
7190 Roo.MessageBox.alert(Roo.encode(rdata));
7193 var data = rdata.data;
7195 if (this.uploadComplete) {
7196 Roo.MessageBox.hide();
7201 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7202 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7205 this.uploadProgress.defer(2000,this);
7208 failure: function(data) {
7209 Roo.log('progress url failed ');
7220 // run get Values on the form, so it syncs any secondary forms.
7221 this.form.getValues();
7223 var o = this.options;
7224 var method = this.getMethod();
7225 var isPost = method == 'POST';
7226 if(o.clientValidation === false || this.form.isValid()){
7228 if (this.form.progressUrl) {
7229 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7230 (new Date() * 1) + '' + Math.random());
7235 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7236 form:this.form.el.dom,
7237 url:this.getUrl(!isPost),
7239 params:isPost ? this.getParams() : null,
7240 isUpload: this.form.fileUpload
7243 this.uploadProgress();
7245 }else if (o.clientValidation !== false){ // client validation failed
7246 this.failureType = Roo.form.Action.CLIENT_INVALID;
7247 this.form.afterAction(this, false);
7251 success : function(response)
7253 this.uploadComplete= true;
7254 if (this.haveProgress) {
7255 Roo.MessageBox.hide();
7259 var result = this.processResponse(response);
7260 if(result === true || result.success){
7261 this.form.afterAction(this, true);
7265 this.form.markInvalid(result.errors);
7266 this.failureType = Roo.form.Action.SERVER_INVALID;
7268 this.form.afterAction(this, false);
7270 failure : function(response)
7272 this.uploadComplete= true;
7273 if (this.haveProgress) {
7274 Roo.MessageBox.hide();
7277 this.response = response;
7278 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7279 this.form.afterAction(this, false);
7282 handleResponse : function(response){
7283 if(this.form.errorReader){
7284 var rs = this.form.errorReader.read(response);
7287 for(var i = 0, len = rs.records.length; i < len; i++) {
7288 var r = rs.records[i];
7292 if(errors.length < 1){
7296 success : rs.success,
7302 ret = Roo.decode(response.responseText);
7306 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7316 Roo.form.Action.Load = function(form, options){
7317 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7318 this.reader = this.form.reader;
7321 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7326 Roo.Ajax.request(Roo.apply(
7327 this.createCallback(), {
7328 method:this.getMethod(),
7329 url:this.getUrl(false),
7330 params:this.getParams()
7334 success : function(response){
7336 var result = this.processResponse(response);
7337 if(result === true || !result.success || !result.data){
7338 this.failureType = Roo.form.Action.LOAD_FAILURE;
7339 this.form.afterAction(this, false);
7342 this.form.clearInvalid();
7343 this.form.setValues(result.data);
7344 this.form.afterAction(this, true);
7347 handleResponse : function(response){
7348 if(this.form.reader){
7349 var rs = this.form.reader.read(response);
7350 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7352 success : rs.success,
7356 return Roo.decode(response.responseText);
7360 Roo.form.Action.ACTION_TYPES = {
7361 'load' : Roo.form.Action.Load,
7362 'submit' : Roo.form.Action.Submit
7371 * @class Roo.bootstrap.Form
7372 * @extends Roo.bootstrap.Component
7373 * Bootstrap Form class
7374 * @cfg {String} method GET | POST (default POST)
7375 * @cfg {String} labelAlign top | left (default top)
7376 * @cfg {String} align left | right - for navbars
7377 * @cfg {Boolean} loadMask load mask when submit (default true)
7382 * @param {Object} config The config object
7386 Roo.bootstrap.Form = function(config){
7387 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7390 * @event clientvalidation
7391 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7392 * @param {Form} this
7393 * @param {Boolean} valid true if the form has passed client-side validation
7395 clientvalidation: true,
7397 * @event beforeaction
7398 * Fires before any action is performed. Return false to cancel the action.
7399 * @param {Form} this
7400 * @param {Action} action The action to be performed
7404 * @event actionfailed
7405 * Fires when an action fails.
7406 * @param {Form} this
7407 * @param {Action} action The action that failed
7409 actionfailed : true,
7411 * @event actioncomplete
7412 * Fires when an action is completed.
7413 * @param {Form} this
7414 * @param {Action} action The action that completed
7416 actioncomplete : true
7421 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7424 * @cfg {String} method
7425 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7430 * The URL to use for form actions if one isn't supplied in the action options.
7433 * @cfg {Boolean} fileUpload
7434 * Set to true if this form is a file upload.
7438 * @cfg {Object} baseParams
7439 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7443 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7447 * @cfg {Sting} align (left|right) for navbar forms
7452 activeAction : null,
7455 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7456 * element by passing it or its id or mask the form itself by passing in true.
7459 waitMsgTarget : false,
7463 getAutoCreate : function(){
7467 method : this.method || 'POST',
7468 id : this.id || Roo.id(),
7471 if (this.parent().xtype.match(/^Nav/)) {
7472 cfg.cls = 'navbar-form navbar-' + this.align;
7476 if (this.labelAlign == 'left' ) {
7477 cfg.cls += ' form-horizontal';
7483 initEvents : function()
7485 this.el.on('submit', this.onSubmit, this);
7486 // this was added as random key presses on the form where triggering form submit.
7487 this.el.on('keypress', function(e) {
7488 if (e.getCharCode() != 13) {
7491 // we might need to allow it for textareas.. and some other items.
7492 // check e.getTarget().
7494 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7498 Roo.log("keypress blocked");
7506 onSubmit : function(e){
7511 * Returns true if client-side validation on the form is successful.
7514 isValid : function(){
7515 var items = this.getItems();
7517 items.each(function(f){
7526 * Returns true if any fields in this form have changed since their original load.
7529 isDirty : function(){
7531 var items = this.getItems();
7532 items.each(function(f){
7542 * Performs a predefined action (submit or load) or custom actions you define on this form.
7543 * @param {String} actionName The name of the action type
7544 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7545 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7546 * accept other config options):
7548 Property Type Description
7549 ---------------- --------------- ----------------------------------------------------------------------------------
7550 url String The url for the action (defaults to the form's url)
7551 method String The form method to use (defaults to the form's method, or POST if not defined)
7552 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7553 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7554 validate the form on the client (defaults to false)
7556 * @return {BasicForm} this
7558 doAction : function(action, options){
7559 if(typeof action == 'string'){
7560 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7562 if(this.fireEvent('beforeaction', this, action) !== false){
7563 this.beforeAction(action);
7564 action.run.defer(100, action);
7570 beforeAction : function(action){
7571 var o = action.options;
7574 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7576 // not really supported yet.. ??
7578 //if(this.waitMsgTarget === true){
7579 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7580 //}else if(this.waitMsgTarget){
7581 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7582 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7584 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7590 afterAction : function(action, success){
7591 this.activeAction = null;
7592 var o = action.options;
7594 //if(this.waitMsgTarget === true){
7596 //}else if(this.waitMsgTarget){
7597 // this.waitMsgTarget.unmask();
7599 // Roo.MessageBox.updateProgress(1);
7600 // Roo.MessageBox.hide();
7607 Roo.callback(o.success, o.scope, [this, action]);
7608 this.fireEvent('actioncomplete', this, action);
7612 // failure condition..
7613 // we have a scenario where updates need confirming.
7614 // eg. if a locking scenario exists..
7615 // we look for { errors : { needs_confirm : true }} in the response.
7617 (typeof(action.result) != 'undefined') &&
7618 (typeof(action.result.errors) != 'undefined') &&
7619 (typeof(action.result.errors.needs_confirm) != 'undefined')
7622 Roo.log("not supported yet");
7625 Roo.MessageBox.confirm(
7626 "Change requires confirmation",
7627 action.result.errorMsg,
7632 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7642 Roo.callback(o.failure, o.scope, [this, action]);
7643 // show an error message if no failed handler is set..
7644 if (!this.hasListener('actionfailed')) {
7645 Roo.log("need to add dialog support");
7647 Roo.MessageBox.alert("Error",
7648 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7649 action.result.errorMsg :
7650 "Saving Failed, please check your entries or try again"
7655 this.fireEvent('actionfailed', this, action);
7660 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7661 * @param {String} id The value to search for
7664 findField : function(id){
7665 var items = this.getItems();
7666 var field = items.get(id);
7668 items.each(function(f){
7669 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7676 return field || null;
7679 * Mark fields in this form invalid in bulk.
7680 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7681 * @return {BasicForm} this
7683 markInvalid : function(errors){
7684 if(errors instanceof Array){
7685 for(var i = 0, len = errors.length; i < len; i++){
7686 var fieldError = errors[i];
7687 var f = this.findField(fieldError.id);
7689 f.markInvalid(fieldError.msg);
7695 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7696 field.markInvalid(errors[id]);
7700 //Roo.each(this.childForms || [], function (f) {
7701 // f.markInvalid(errors);
7708 * Set values for fields in this form in bulk.
7709 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7710 * @return {BasicForm} this
7712 setValues : function(values){
7713 if(values instanceof Array){ // array of objects
7714 for(var i = 0, len = values.length; i < len; i++){
7716 var f = this.findField(v.id);
7718 f.setValue(v.value);
7719 if(this.trackResetOnLoad){
7720 f.originalValue = f.getValue();
7724 }else{ // object hash
7727 if(typeof values[id] != 'function' && (field = this.findField(id))){
7729 if (field.setFromData &&
7731 field.displayField &&
7732 // combos' with local stores can
7733 // be queried via setValue()
7734 // to set their value..
7735 (field.store && !field.store.isLocal)
7739 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7740 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7741 field.setFromData(sd);
7744 field.setValue(values[id]);
7748 if(this.trackResetOnLoad){
7749 field.originalValue = field.getValue();
7755 //Roo.each(this.childForms || [], function (f) {
7756 // f.setValues(values);
7763 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7764 * they are returned as an array.
7765 * @param {Boolean} asString
7768 getValues : function(asString){
7769 //if (this.childForms) {
7770 // copy values from the child forms
7771 // Roo.each(this.childForms, function (f) {
7772 // this.setValues(f.getValues());
7778 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7779 if(asString === true){
7782 return Roo.urlDecode(fs);
7786 * Returns the fields in this form as an object with key/value pairs.
7787 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7790 getFieldValues : function(with_hidden)
7792 var items = this.getItems();
7794 items.each(function(f){
7798 var v = f.getValue();
7799 if (f.inputType =='radio') {
7800 if (typeof(ret[f.getName()]) == 'undefined') {
7801 ret[f.getName()] = ''; // empty..
7804 if (!f.el.dom.checked) {
7812 // not sure if this supported any more..
7813 if ((typeof(v) == 'object') && f.getRawValue) {
7814 v = f.getRawValue() ; // dates..
7816 // combo boxes where name != hiddenName...
7817 if (f.name != f.getName()) {
7818 ret[f.name] = f.getRawValue();
7820 ret[f.getName()] = v;
7827 * Clears all invalid messages in this form.
7828 * @return {BasicForm} this
7830 clearInvalid : function(){
7831 var items = this.getItems();
7833 items.each(function(f){
7844 * @return {BasicForm} this
7847 var items = this.getItems();
7848 items.each(function(f){
7852 Roo.each(this.childForms || [], function (f) {
7859 getItems : function()
7861 var r=new Roo.util.MixedCollection(false, function(o){
7862 return o.id || (o.id = Roo.id());
7864 var iter = function(el) {
7871 Roo.each(el.items,function(e) {
7891 * Ext JS Library 1.1.1
7892 * Copyright(c) 2006-2007, Ext JS, LLC.
7894 * Originally Released Under LGPL - original licence link has changed is not relivant.
7897 * <script type="text/javascript">
7900 * @class Roo.form.VTypes
7901 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7904 Roo.form.VTypes = function(){
7905 // closure these in so they are only created once.
7906 var alpha = /^[a-zA-Z_]+$/;
7907 var alphanum = /^[a-zA-Z0-9_]+$/;
7908 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7909 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7911 // All these messages and functions are configurable
7914 * The function used to validate email addresses
7915 * @param {String} value The email address
7917 'email' : function(v){
7918 return email.test(v);
7921 * The error text to display when the email validation function returns false
7924 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7926 * The keystroke filter mask to be applied on email input
7929 'emailMask' : /[a-z0-9_\.\-@]/i,
7932 * The function used to validate URLs
7933 * @param {String} value The URL
7935 'url' : function(v){
7939 * The error text to display when the url validation function returns false
7942 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7945 * The function used to validate alpha values
7946 * @param {String} value The value
7948 'alpha' : function(v){
7949 return alpha.test(v);
7952 * The error text to display when the alpha validation function returns false
7955 'alphaText' : 'This field should only contain letters and _',
7957 * The keystroke filter mask to be applied on alpha input
7960 'alphaMask' : /[a-z_]/i,
7963 * The function used to validate alphanumeric values
7964 * @param {String} value The value
7966 'alphanum' : function(v){
7967 return alphanum.test(v);
7970 * The error text to display when the alphanumeric validation function returns false
7973 'alphanumText' : 'This field should only contain letters, numbers and _',
7975 * The keystroke filter mask to be applied on alphanumeric input
7978 'alphanumMask' : /[a-z0-9_]/i
7988 * @class Roo.bootstrap.Input
7989 * @extends Roo.bootstrap.Component
7990 * Bootstrap Input class
7991 * @cfg {Boolean} disabled is it disabled
7992 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7993 * @cfg {String} name name of the input
7994 * @cfg {string} fieldLabel - the label associated
7995 * @cfg {string} placeholder - placeholder to put in text.
7996 * @cfg {string} before - input group add on before
7997 * @cfg {string} after - input group add on after
7998 * @cfg {string} size - (lg|sm) or leave empty..
7999 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8000 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8001 * @cfg {Number} md colspan out of 12 for computer-sized screens
8002 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8003 * @cfg {string} value default value of the input
8004 * @cfg {Number} labelWidth set the width of label (0-12)
8005 * @cfg {String} labelAlign (top|left)
8006 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8007 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8009 * @cfg {String} align (left|center|right) Default left
8010 * @cfg {Boolean} forceFeedback (true|false) Default false
8016 * Create a new Input
8017 * @param {Object} config The config object
8020 Roo.bootstrap.Input = function(config){
8021 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8026 * Fires when this field receives input focus.
8027 * @param {Roo.form.Field} this
8032 * Fires when this field loses input focus.
8033 * @param {Roo.form.Field} this
8038 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8039 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8040 * @param {Roo.form.Field} this
8041 * @param {Roo.EventObject} e The event object
8046 * Fires just before the field blurs if the field value has changed.
8047 * @param {Roo.form.Field} this
8048 * @param {Mixed} newValue The new value
8049 * @param {Mixed} oldValue The original value
8054 * Fires after the field has been marked as invalid.
8055 * @param {Roo.form.Field} this
8056 * @param {String} msg The validation message
8061 * Fires after the field has been validated with no errors.
8062 * @param {Roo.form.Field} this
8067 * Fires after the key up
8068 * @param {Roo.form.Field} this
8069 * @param {Roo.EventObject} e The event Object
8075 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8077 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8078 automatic validation (defaults to "keyup").
8080 validationEvent : "keyup",
8082 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8084 validateOnBlur : true,
8086 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8088 validationDelay : 250,
8090 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8092 focusClass : "x-form-focus", // not needed???
8096 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8098 invalidClass : "has-warning",
8101 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8103 validClass : "has-success",
8106 * @cfg {Boolean} hasFeedback (true|false) default true
8111 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8113 invalidFeedbackClass : "glyphicon-warning-sign",
8116 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8118 validFeedbackClass : "glyphicon-ok",
8121 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8123 selectOnFocus : false,
8126 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8130 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8135 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8137 disableKeyFilter : false,
8140 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8144 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8148 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8150 blankText : "This field is required",
8153 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8157 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8159 maxLength : Number.MAX_VALUE,
8161 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8163 minLengthText : "The minimum length for this field is {0}",
8165 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8167 maxLengthText : "The maximum length for this field is {0}",
8171 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8172 * If available, this function will be called only after the basic validators all return true, and will be passed the
8173 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8177 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8178 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8179 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8183 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8187 autocomplete: false,
8206 formatedValue : false,
8207 forceFeedback : false,
8209 parentLabelAlign : function()
8212 while (parent.parent()) {
8213 parent = parent.parent();
8214 if (typeof(parent.labelAlign) !='undefined') {
8215 return parent.labelAlign;
8222 getAutoCreate : function(){
8224 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8232 if(this.inputType != 'hidden'){
8233 cfg.cls = 'form-group' //input-group
8239 type : this.inputType,
8241 cls : 'form-control',
8242 placeholder : this.placeholder || '',
8243 autocomplete : this.autocomplete || 'new-password'
8248 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8251 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8252 input.maxLength = this.maxLength;
8255 if (this.disabled) {
8256 input.disabled=true;
8259 if (this.readOnly) {
8260 input.readonly=true;
8264 input.name = this.name;
8267 input.cls += ' input-' + this.size;
8270 ['xs','sm','md','lg'].map(function(size){
8271 if (settings[size]) {
8272 cfg.cls += ' col-' + size + '-' + settings[size];
8276 var inputblock = input;
8280 cls: 'glyphicon form-control-feedback'
8283 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8286 cls : 'has-feedback',
8294 if (this.before || this.after) {
8297 cls : 'input-group',
8301 if (this.before && typeof(this.before) == 'string') {
8303 inputblock.cn.push({
8305 cls : 'roo-input-before input-group-addon',
8309 if (this.before && typeof(this.before) == 'object') {
8310 this.before = Roo.factory(this.before);
8312 inputblock.cn.push({
8314 cls : 'roo-input-before input-group-' +
8315 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8319 inputblock.cn.push(input);
8321 if (this.after && typeof(this.after) == 'string') {
8322 inputblock.cn.push({
8324 cls : 'roo-input-after input-group-addon',
8328 if (this.after && typeof(this.after) == 'object') {
8329 this.after = Roo.factory(this.after);
8331 inputblock.cn.push({
8333 cls : 'roo-input-after input-group-' +
8334 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8338 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8339 inputblock.cls += ' has-feedback';
8340 inputblock.cn.push(feedback);
8344 if (align ==='left' && this.fieldLabel.length) {
8351 cls : 'control-label col-sm-' + this.labelWidth,
8352 html : this.fieldLabel
8356 cls : "col-sm-" + (12 - this.labelWidth),
8363 } else if ( this.fieldLabel.length) {
8369 //cls : 'input-group-addon',
8370 html : this.fieldLabel
8389 if (this.parentType === 'Navbar' && this.parent().bar) {
8390 cfg.cls += ' navbar-form';
8392 if (this.parentType === 'NavGroup') {
8393 cfg.cls += ' navbar-form';
8400 * return the real input element.
8402 inputEl: function ()
8404 return this.el.select('input.form-control',true).first();
8407 tooltipEl : function()
8409 return this.inputEl();
8412 setDisabled : function(v)
8414 var i = this.inputEl().dom;
8416 i.removeAttribute('disabled');
8420 i.setAttribute('disabled','true');
8422 initEvents : function()
8425 this.inputEl().on("keydown" , this.fireKey, this);
8426 this.inputEl().on("focus", this.onFocus, this);
8427 this.inputEl().on("blur", this.onBlur, this);
8429 this.inputEl().relayEvent('keyup', this);
8431 // reference to original value for reset
8432 this.originalValue = this.getValue();
8433 //Roo.form.TextField.superclass.initEvents.call(this);
8434 if(this.validationEvent == 'keyup'){
8435 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8436 this.inputEl().on('keyup', this.filterValidation, this);
8438 else if(this.validationEvent !== false){
8439 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8442 if(this.selectOnFocus){
8443 this.on("focus", this.preFocus, this);
8446 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8447 this.inputEl().on("keypress", this.filterKeys, this);
8450 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8451 this.el.on("click", this.autoSize, this);
8454 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8455 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8458 if (typeof(this.before) == 'object') {
8459 this.before.render(this.el.select('.roo-input-before',true).first());
8461 if (typeof(this.after) == 'object') {
8462 this.after.render(this.el.select('.roo-input-after',true).first());
8467 filterValidation : function(e){
8468 if(!e.isNavKeyPress()){
8469 this.validationTask.delay(this.validationDelay);
8473 * Validates the field value
8474 * @return {Boolean} True if the value is valid, else false
8476 validate : function(){
8477 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8478 if(this.disabled || this.validateValue(this.getRawValue())){
8489 * Validates a value according to the field's validation rules and marks the field as invalid
8490 * if the validation fails
8491 * @param {Mixed} value The value to validate
8492 * @return {Boolean} True if the value is valid, else false
8494 validateValue : function(value){
8495 if(value.length < 1) { // if it's blank
8496 if(this.allowBlank){
8502 if(value.length < this.minLength){
8505 if(value.length > this.maxLength){
8509 var vt = Roo.form.VTypes;
8510 if(!vt[this.vtype](value, this)){
8514 if(typeof this.validator == "function"){
8515 var msg = this.validator(value);
8521 if(this.regex && !this.regex.test(value)){
8531 fireKey : function(e){
8532 //Roo.log('field ' + e.getKey());
8533 if(e.isNavKeyPress()){
8534 this.fireEvent("specialkey", this, e);
8537 focus : function (selectText){
8539 this.inputEl().focus();
8540 if(selectText === true){
8541 this.inputEl().dom.select();
8547 onFocus : function(){
8548 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8549 // this.el.addClass(this.focusClass);
8552 this.hasFocus = true;
8553 this.startValue = this.getValue();
8554 this.fireEvent("focus", this);
8558 beforeBlur : Roo.emptyFn,
8562 onBlur : function(){
8564 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8565 //this.el.removeClass(this.focusClass);
8567 this.hasFocus = false;
8568 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8571 var v = this.getValue();
8572 if(String(v) !== String(this.startValue)){
8573 this.fireEvent('change', this, v, this.startValue);
8575 this.fireEvent("blur", this);
8579 * Resets the current field value to the originally loaded value and clears any validation messages
8582 this.setValue(this.originalValue);
8586 * Returns the name of the field
8587 * @return {Mixed} name The name field
8589 getName: function(){
8593 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8594 * @return {Mixed} value The field value
8596 getValue : function(){
8598 var v = this.inputEl().getValue();
8603 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8604 * @return {Mixed} value The field value
8606 getRawValue : function(){
8607 var v = this.inputEl().getValue();
8613 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8614 * @param {Mixed} value The value to set
8616 setRawValue : function(v){
8617 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8620 selectText : function(start, end){
8621 var v = this.getRawValue();
8623 start = start === undefined ? 0 : start;
8624 end = end === undefined ? v.length : end;
8625 var d = this.inputEl().dom;
8626 if(d.setSelectionRange){
8627 d.setSelectionRange(start, end);
8628 }else if(d.createTextRange){
8629 var range = d.createTextRange();
8630 range.moveStart("character", start);
8631 range.moveEnd("character", v.length-end);
8638 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8639 * @param {Mixed} value The value to set
8641 setValue : function(v){
8644 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8650 processValue : function(value){
8651 if(this.stripCharsRe){
8652 var newValue = value.replace(this.stripCharsRe, '');
8653 if(newValue !== value){
8654 this.setRawValue(newValue);
8661 preFocus : function(){
8663 if(this.selectOnFocus){
8664 this.inputEl().dom.select();
8667 filterKeys : function(e){
8669 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8672 var c = e.getCharCode(), cc = String.fromCharCode(c);
8673 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8676 if(!this.maskRe.test(cc)){
8681 * Clear any invalid styles/messages for this field
8683 clearInvalid : function(){
8685 if(!this.el || this.preventMark){ // not rendered
8689 var label = this.el.select('label', true).first();
8690 var icon = this.el.select('i.fa-star', true).first();
8696 this.el.removeClass(this.invalidClass);
8698 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8700 var feedback = this.el.select('.form-control-feedback', true).first();
8703 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8708 this.fireEvent('valid', this);
8712 * Mark this field as valid
8714 markValid : function()
8716 if(!this.el || this.preventMark){ // not rendered
8720 this.el.removeClass([this.invalidClass, this.validClass]);
8722 var feedback = this.el.select('.form-control-feedback', true).first();
8725 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8728 if(this.disabled || this.allowBlank){
8732 var formGroup = this.el.findParent('.form-group', false, true);
8736 var label = formGroup.select('label', true).first();
8737 var icon = formGroup.select('i.fa-star', true).first();
8744 this.el.addClass(this.validClass);
8746 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8748 var feedback = this.el.select('.form-control-feedback', true).first();
8751 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8752 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8757 this.fireEvent('valid', this);
8761 * Mark this field as invalid
8762 * @param {String} msg The validation message
8764 markInvalid : function(msg)
8766 if(!this.el || this.preventMark){ // not rendered
8770 this.el.removeClass([this.invalidClass, this.validClass]);
8772 var feedback = this.el.select('.form-control-feedback', true).first();
8775 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8778 if(this.disabled || this.allowBlank){
8782 var formGroup = this.el.findParent('.form-group', false, true);
8785 var label = formGroup.select('label', true).first();
8786 var icon = formGroup.select('i.fa-star', true).first();
8788 if(!this.getValue().length && label && !icon){
8789 this.el.findParent('.form-group', false, true).createChild({
8791 cls : 'text-danger fa fa-lg fa-star',
8792 tooltip : 'This field is required',
8793 style : 'margin-right:5px;'
8799 this.el.addClass(this.invalidClass);
8801 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8803 var feedback = this.el.select('.form-control-feedback', true).first();
8806 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8808 if(this.getValue().length || this.forceFeedback){
8809 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8816 this.fireEvent('invalid', this, msg);
8819 SafariOnKeyDown : function(event)
8821 // this is a workaround for a password hang bug on chrome/ webkit.
8823 var isSelectAll = false;
8825 if(this.inputEl().dom.selectionEnd > 0){
8826 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8828 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8829 event.preventDefault();
8834 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8836 event.preventDefault();
8837 // this is very hacky as keydown always get's upper case.
8839 var cc = String.fromCharCode(event.getCharCode());
8840 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8844 adjustWidth : function(tag, w){
8845 tag = tag.toLowerCase();
8846 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8847 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8851 if(tag == 'textarea'){
8854 }else if(Roo.isOpera){
8858 if(tag == 'textarea'){
8877 * @class Roo.bootstrap.TextArea
8878 * @extends Roo.bootstrap.Input
8879 * Bootstrap TextArea class
8880 * @cfg {Number} cols Specifies the visible width of a text area
8881 * @cfg {Number} rows Specifies the visible number of lines in a text area
8882 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8883 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8884 * @cfg {string} html text
8887 * Create a new TextArea
8888 * @param {Object} config The config object
8891 Roo.bootstrap.TextArea = function(config){
8892 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8896 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8906 getAutoCreate : function(){
8908 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8919 value : this.value || '',
8920 html: this.html || '',
8921 cls : 'form-control',
8922 placeholder : this.placeholder || ''
8926 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8927 input.maxLength = this.maxLength;
8931 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8935 input.cols = this.cols;
8938 if (this.readOnly) {
8939 input.readonly = true;
8943 input.name = this.name;
8947 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8951 ['xs','sm','md','lg'].map(function(size){
8952 if (settings[size]) {
8953 cfg.cls += ' col-' + size + '-' + settings[size];
8957 var inputblock = input;
8959 if(this.hasFeedback && !this.allowBlank){
8963 cls: 'glyphicon form-control-feedback'
8967 cls : 'has-feedback',
8976 if (this.before || this.after) {
8979 cls : 'input-group',
8983 inputblock.cn.push({
8985 cls : 'input-group-addon',
8990 inputblock.cn.push(input);
8992 if(this.hasFeedback && !this.allowBlank){
8993 inputblock.cls += ' has-feedback';
8994 inputblock.cn.push(feedback);
8998 inputblock.cn.push({
9000 cls : 'input-group-addon',
9007 if (align ==='left' && this.fieldLabel.length) {
9008 // Roo.log("left and has label");
9014 cls : 'control-label col-sm-' + this.labelWidth,
9015 html : this.fieldLabel
9019 cls : "col-sm-" + (12 - this.labelWidth),
9026 } else if ( this.fieldLabel.length) {
9027 // Roo.log(" label");
9032 //cls : 'input-group-addon',
9033 html : this.fieldLabel
9043 // Roo.log(" no label && no align");
9053 if (this.disabled) {
9054 input.disabled=true;
9061 * return the real textarea element.
9063 inputEl: function ()
9065 return this.el.select('textarea.form-control',true).first();
9069 * Clear any invalid styles/messages for this field
9071 clearInvalid : function()
9074 if(!this.el || this.preventMark){ // not rendered
9078 var label = this.el.select('label', true).first();
9079 var icon = this.el.select('i.fa-star', true).first();
9085 this.el.removeClass(this.invalidClass);
9087 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9089 var feedback = this.el.select('.form-control-feedback', true).first();
9092 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9097 this.fireEvent('valid', this);
9101 * Mark this field as valid
9103 markValid : function()
9105 if(!this.el || this.preventMark){ // not rendered
9109 this.el.removeClass([this.invalidClass, this.validClass]);
9111 var feedback = this.el.select('.form-control-feedback', true).first();
9114 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9117 if(this.disabled || this.allowBlank){
9121 var label = this.el.select('label', true).first();
9122 var icon = this.el.select('i.fa-star', true).first();
9128 this.el.addClass(this.validClass);
9130 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9132 var feedback = this.el.select('.form-control-feedback', true).first();
9135 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9136 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9141 this.fireEvent('valid', this);
9145 * Mark this field as invalid
9146 * @param {String} msg The validation message
9148 markInvalid : function(msg)
9150 if(!this.el || this.preventMark){ // not rendered
9154 this.el.removeClass([this.invalidClass, this.validClass]);
9156 var feedback = this.el.select('.form-control-feedback', true).first();
9159 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9162 if(this.disabled || this.allowBlank){
9166 var label = this.el.select('label', true).first();
9167 var icon = this.el.select('i.fa-star', true).first();
9169 if(!this.getValue().length && label && !icon){
9170 this.el.createChild({
9172 cls : 'text-danger fa fa-lg fa-star',
9173 tooltip : 'This field is required',
9174 style : 'margin-right:5px;'
9178 this.el.addClass(this.invalidClass);
9180 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9182 var feedback = this.el.select('.form-control-feedback', true).first();
9185 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9187 if(this.getValue().length || this.forceFeedback){
9188 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9195 this.fireEvent('invalid', this, msg);
9203 * trigger field - base class for combo..
9208 * @class Roo.bootstrap.TriggerField
9209 * @extends Roo.bootstrap.Input
9210 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9211 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9212 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9213 * for which you can provide a custom implementation. For example:
9215 var trigger = new Roo.bootstrap.TriggerField();
9216 trigger.onTriggerClick = myTriggerFn;
9217 trigger.applyTo('my-field');
9220 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9221 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9222 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9223 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9224 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9227 * Create a new TriggerField.
9228 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9229 * to the base TextField)
9231 Roo.bootstrap.TriggerField = function(config){
9232 this.mimicing = false;
9233 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9236 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9238 * @cfg {String} triggerClass A CSS class to apply to the trigger
9241 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9246 * @cfg {Boolean} removable (true|false) special filter default false
9250 /** @cfg {Boolean} grow @hide */
9251 /** @cfg {Number} growMin @hide */
9252 /** @cfg {Number} growMax @hide */
9258 autoSize: Roo.emptyFn,
9265 actionMode : 'wrap',
9270 getAutoCreate : function(){
9272 var align = this.labelAlign || this.parentLabelAlign();
9277 cls: 'form-group' //input-group
9284 type : this.inputType,
9285 cls : 'form-control',
9286 autocomplete: 'new-password',
9287 placeholder : this.placeholder || ''
9291 input.name = this.name;
9294 input.cls += ' input-' + this.size;
9297 if (this.disabled) {
9298 input.disabled=true;
9301 var inputblock = input;
9303 if(this.hasFeedback && !this.allowBlank){
9307 cls: 'glyphicon form-control-feedback'
9310 if(this.removable && !this.editable && !this.tickable){
9312 cls : 'has-feedback',
9318 cls : 'roo-combo-removable-btn close'
9325 cls : 'has-feedback',
9334 if(this.removable && !this.editable && !this.tickable){
9336 cls : 'roo-removable',
9342 cls : 'roo-combo-removable-btn close'
9349 if (this.before || this.after) {
9352 cls : 'input-group',
9356 inputblock.cn.push({
9358 cls : 'input-group-addon',
9363 inputblock.cn.push(input);
9365 if(this.hasFeedback && !this.allowBlank){
9366 inputblock.cls += ' has-feedback';
9367 inputblock.cn.push(feedback);
9371 inputblock.cn.push({
9373 cls : 'input-group-addon',
9386 cls: 'form-hidden-field'
9400 cls: 'form-hidden-field'
9404 cls: 'roo-select2-choices',
9408 cls: 'roo-select2-search-field',
9421 cls: 'roo-select2-container input-group',
9426 // cls: 'typeahead typeahead-long dropdown-menu',
9427 // style: 'display:none'
9432 if(!this.multiple && this.showToggleBtn){
9438 if (this.caret != false) {
9441 cls: 'fa fa-' + this.caret
9448 cls : 'input-group-addon btn dropdown-toggle',
9453 cls: 'combobox-clear',
9467 combobox.cls += ' roo-select2-container-multi';
9470 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9472 // Roo.log("left and has label");
9478 cls : 'control-label col-sm-' + this.labelWidth,
9479 html : this.fieldLabel
9483 cls : "col-sm-" + (12 - this.labelWidth),
9490 } else if ( this.fieldLabel.length) {
9491 // Roo.log(" label");
9496 //cls : 'input-group-addon',
9497 html : this.fieldLabel
9507 // Roo.log(" no label && no align");
9514 ['xs','sm','md','lg'].map(function(size){
9515 if (settings[size]) {
9516 cfg.cls += ' col-' + size + '-' + settings[size];
9527 onResize : function(w, h){
9528 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9529 // if(typeof w == 'number'){
9530 // var x = w - this.trigger.getWidth();
9531 // this.inputEl().setWidth(this.adjustWidth('input', x));
9532 // this.trigger.setStyle('left', x+'px');
9537 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9540 getResizeEl : function(){
9541 return this.inputEl();
9545 getPositionEl : function(){
9546 return this.inputEl();
9550 alignErrorIcon : function(){
9551 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9555 initEvents : function(){
9559 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9560 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9561 if(!this.multiple && this.showToggleBtn){
9562 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9563 if(this.hideTrigger){
9564 this.trigger.setDisplayed(false);
9566 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9570 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9573 if(this.removable && !this.editable && !this.tickable){
9574 var close = this.closeTriggerEl();
9577 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9578 close.on('click', this.removeBtnClick, this, close);
9582 //this.trigger.addClassOnOver('x-form-trigger-over');
9583 //this.trigger.addClassOnClick('x-form-trigger-click');
9586 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9590 closeTriggerEl : function()
9592 var close = this.el.select('.roo-combo-removable-btn', true).first();
9593 return close ? close : false;
9596 removeBtnClick : function(e, h, el)
9600 if(this.fireEvent("remove", this) !== false){
9602 this.fireEvent("afterremove", this)
9606 createList : function()
9608 this.list = Roo.get(document.body).createChild({
9610 cls: 'typeahead typeahead-long dropdown-menu',
9611 style: 'display:none'
9614 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9619 initTrigger : function(){
9624 onDestroy : function(){
9626 this.trigger.removeAllListeners();
9627 // this.trigger.remove();
9630 // this.wrap.remove();
9632 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9636 onFocus : function(){
9637 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9640 this.wrap.addClass('x-trigger-wrap-focus');
9641 this.mimicing = true;
9642 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9643 if(this.monitorTab){
9644 this.el.on("keydown", this.checkTab, this);
9651 checkTab : function(e){
9652 if(e.getKey() == e.TAB){
9658 onBlur : function(){
9663 mimicBlur : function(e, t){
9665 if(!this.wrap.contains(t) && this.validateBlur()){
9672 triggerBlur : function(){
9673 this.mimicing = false;
9674 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9675 if(this.monitorTab){
9676 this.el.un("keydown", this.checkTab, this);
9678 //this.wrap.removeClass('x-trigger-wrap-focus');
9679 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9683 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9684 validateBlur : function(e, t){
9689 onDisable : function(){
9690 this.inputEl().dom.disabled = true;
9691 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9693 // this.wrap.addClass('x-item-disabled');
9698 onEnable : function(){
9699 this.inputEl().dom.disabled = false;
9700 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9702 // this.el.removeClass('x-item-disabled');
9707 onShow : function(){
9708 var ae = this.getActionEl();
9711 ae.dom.style.display = '';
9712 ae.dom.style.visibility = 'visible';
9718 onHide : function(){
9719 var ae = this.getActionEl();
9720 ae.dom.style.display = 'none';
9724 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9725 * by an implementing function.
9727 * @param {EventObject} e
9729 onTriggerClick : Roo.emptyFn
9733 * Ext JS Library 1.1.1
9734 * Copyright(c) 2006-2007, Ext JS, LLC.
9736 * Originally Released Under LGPL - original licence link has changed is not relivant.
9739 * <script type="text/javascript">
9744 * @class Roo.data.SortTypes
9746 * Defines the default sorting (casting?) comparison functions used when sorting data.
9748 Roo.data.SortTypes = {
9750 * Default sort that does nothing
9751 * @param {Mixed} s The value being converted
9752 * @return {Mixed} The comparison value
9759 * The regular expression used to strip tags
9763 stripTagsRE : /<\/?[^>]+>/gi,
9766 * Strips all HTML tags to sort on text only
9767 * @param {Mixed} s The value being converted
9768 * @return {String} The comparison value
9770 asText : function(s){
9771 return String(s).replace(this.stripTagsRE, "");
9775 * Strips all HTML tags to sort on text only - Case insensitive
9776 * @param {Mixed} s The value being converted
9777 * @return {String} The comparison value
9779 asUCText : function(s){
9780 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9784 * Case insensitive string
9785 * @param {Mixed} s The value being converted
9786 * @return {String} The comparison value
9788 asUCString : function(s) {
9789 return String(s).toUpperCase();
9794 * @param {Mixed} s The value being converted
9795 * @return {Number} The comparison value
9797 asDate : function(s) {
9801 if(s instanceof Date){
9804 return Date.parse(String(s));
9809 * @param {Mixed} s The value being converted
9810 * @return {Float} The comparison value
9812 asFloat : function(s) {
9813 var val = parseFloat(String(s).replace(/,/g, ""));
9822 * @param {Mixed} s The value being converted
9823 * @return {Number} The comparison value
9825 asInt : function(s) {
9826 var val = parseInt(String(s).replace(/,/g, ""));
9834 * Ext JS Library 1.1.1
9835 * Copyright(c) 2006-2007, Ext JS, LLC.
9837 * Originally Released Under LGPL - original licence link has changed is not relivant.
9840 * <script type="text/javascript">
9844 * @class Roo.data.Record
9845 * Instances of this class encapsulate both record <em>definition</em> information, and record
9846 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9847 * to access Records cached in an {@link Roo.data.Store} object.<br>
9849 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9850 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9853 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9855 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9856 * {@link #create}. The parameters are the same.
9857 * @param {Array} data An associative Array of data values keyed by the field name.
9858 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9859 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9860 * not specified an integer id is generated.
9862 Roo.data.Record = function(data, id){
9863 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9868 * Generate a constructor for a specific record layout.
9869 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9870 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9871 * Each field definition object may contain the following properties: <ul>
9872 * <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,
9873 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9874 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9875 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9876 * is being used, then this is a string containing the javascript expression to reference the data relative to
9877 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9878 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9879 * this may be omitted.</p></li>
9880 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9881 * <ul><li>auto (Default, implies no conversion)</li>
9886 * <li>date</li></ul></p></li>
9887 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9888 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9889 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9890 * by the Reader into an object that will be stored in the Record. It is passed the
9891 * following parameters:<ul>
9892 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9894 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9896 * <br>usage:<br><pre><code>
9897 var TopicRecord = Roo.data.Record.create(
9898 {name: 'title', mapping: 'topic_title'},
9899 {name: 'author', mapping: 'username'},
9900 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9901 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9902 {name: 'lastPoster', mapping: 'user2'},
9903 {name: 'excerpt', mapping: 'post_text'}
9906 var myNewRecord = new TopicRecord({
9907 title: 'Do my job please',
9910 lastPost: new Date(),
9911 lastPoster: 'Animal',
9912 excerpt: 'No way dude!'
9914 myStore.add(myNewRecord);
9919 Roo.data.Record.create = function(o){
9921 f.superclass.constructor.apply(this, arguments);
9923 Roo.extend(f, Roo.data.Record);
9924 var p = f.prototype;
9925 p.fields = new Roo.util.MixedCollection(false, function(field){
9928 for(var i = 0, len = o.length; i < len; i++){
9929 p.fields.add(new Roo.data.Field(o[i]));
9931 f.getField = function(name){
9932 return p.fields.get(name);
9937 Roo.data.Record.AUTO_ID = 1000;
9938 Roo.data.Record.EDIT = 'edit';
9939 Roo.data.Record.REJECT = 'reject';
9940 Roo.data.Record.COMMIT = 'commit';
9942 Roo.data.Record.prototype = {
9944 * Readonly flag - true if this record has been modified.
9953 join : function(store){
9958 * Set the named field to the specified value.
9959 * @param {String} name The name of the field to set.
9960 * @param {Object} value The value to set the field to.
9962 set : function(name, value){
9963 if(this.data[name] == value){
9970 if(typeof this.modified[name] == 'undefined'){
9971 this.modified[name] = this.data[name];
9973 this.data[name] = value;
9974 if(!this.editing && this.store){
9975 this.store.afterEdit(this);
9980 * Get the value of the named field.
9981 * @param {String} name The name of the field to get the value of.
9982 * @return {Object} The value of the field.
9984 get : function(name){
9985 return this.data[name];
9989 beginEdit : function(){
9990 this.editing = true;
9995 cancelEdit : function(){
9996 this.editing = false;
9997 delete this.modified;
10001 endEdit : function(){
10002 this.editing = false;
10003 if(this.dirty && this.store){
10004 this.store.afterEdit(this);
10009 * Usually called by the {@link Roo.data.Store} which owns the Record.
10010 * Rejects all changes made to the Record since either creation, or the last commit operation.
10011 * Modified fields are reverted to their original values.
10013 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10014 * of reject operations.
10016 reject : function(){
10017 var m = this.modified;
10019 if(typeof m[n] != "function"){
10020 this.data[n] = m[n];
10023 this.dirty = false;
10024 delete this.modified;
10025 this.editing = false;
10027 this.store.afterReject(this);
10032 * Usually called by the {@link Roo.data.Store} which owns the Record.
10033 * Commits all changes made to the Record since either creation, or the last commit operation.
10035 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10036 * of commit operations.
10038 commit : function(){
10039 this.dirty = false;
10040 delete this.modified;
10041 this.editing = false;
10043 this.store.afterCommit(this);
10048 hasError : function(){
10049 return this.error != null;
10053 clearError : function(){
10058 * Creates a copy of this record.
10059 * @param {String} id (optional) A new record id if you don't want to use this record's id
10062 copy : function(newId) {
10063 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10067 * Ext JS Library 1.1.1
10068 * Copyright(c) 2006-2007, Ext JS, LLC.
10070 * Originally Released Under LGPL - original licence link has changed is not relivant.
10073 * <script type="text/javascript">
10079 * @class Roo.data.Store
10080 * @extends Roo.util.Observable
10081 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10082 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10084 * 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
10085 * has no knowledge of the format of the data returned by the Proxy.<br>
10087 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10088 * instances from the data object. These records are cached and made available through accessor functions.
10090 * Creates a new Store.
10091 * @param {Object} config A config object containing the objects needed for the Store to access data,
10092 * and read the data into Records.
10094 Roo.data.Store = function(config){
10095 this.data = new Roo.util.MixedCollection(false);
10096 this.data.getKey = function(o){
10099 this.baseParams = {};
10101 this.paramNames = {
10106 "multisort" : "_multisort"
10109 if(config && config.data){
10110 this.inlineData = config.data;
10111 delete config.data;
10114 Roo.apply(this, config);
10116 if(this.reader){ // reader passed
10117 this.reader = Roo.factory(this.reader, Roo.data);
10118 this.reader.xmodule = this.xmodule || false;
10119 if(!this.recordType){
10120 this.recordType = this.reader.recordType;
10122 if(this.reader.onMetaChange){
10123 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10127 if(this.recordType){
10128 this.fields = this.recordType.prototype.fields;
10130 this.modified = [];
10134 * @event datachanged
10135 * Fires when the data cache has changed, and a widget which is using this Store
10136 * as a Record cache should refresh its view.
10137 * @param {Store} this
10139 datachanged : true,
10141 * @event metachange
10142 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10143 * @param {Store} this
10144 * @param {Object} meta The JSON metadata
10149 * Fires when Records have been added to the Store
10150 * @param {Store} this
10151 * @param {Roo.data.Record[]} records The array of Records added
10152 * @param {Number} index The index at which the record(s) were added
10157 * Fires when a Record has been removed from the Store
10158 * @param {Store} this
10159 * @param {Roo.data.Record} record The Record that was removed
10160 * @param {Number} index The index at which the record was removed
10165 * Fires when a Record has been updated
10166 * @param {Store} this
10167 * @param {Roo.data.Record} record The Record that was updated
10168 * @param {String} operation The update operation being performed. Value may be one of:
10170 Roo.data.Record.EDIT
10171 Roo.data.Record.REJECT
10172 Roo.data.Record.COMMIT
10178 * Fires when the data cache has been cleared.
10179 * @param {Store} this
10183 * @event beforeload
10184 * Fires before a request is made for a new data object. If the beforeload handler returns false
10185 * the load action will be canceled.
10186 * @param {Store} this
10187 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10191 * @event beforeloadadd
10192 * Fires after a new set of Records has been loaded.
10193 * @param {Store} this
10194 * @param {Roo.data.Record[]} records The Records that were loaded
10195 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10197 beforeloadadd : true,
10200 * Fires after a new set of Records has been loaded, before they are added to the store.
10201 * @param {Store} this
10202 * @param {Roo.data.Record[]} records The Records that were loaded
10203 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10204 * @params {Object} return from reader
10208 * @event loadexception
10209 * Fires if an exception occurs in the Proxy during loading.
10210 * Called with the signature of the Proxy's "loadexception" event.
10211 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10214 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10215 * @param {Object} load options
10216 * @param {Object} jsonData from your request (normally this contains the Exception)
10218 loadexception : true
10222 this.proxy = Roo.factory(this.proxy, Roo.data);
10223 this.proxy.xmodule = this.xmodule || false;
10224 this.relayEvents(this.proxy, ["loadexception"]);
10226 this.sortToggle = {};
10227 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10229 Roo.data.Store.superclass.constructor.call(this);
10231 if(this.inlineData){
10232 this.loadData(this.inlineData);
10233 delete this.inlineData;
10237 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10239 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10240 * without a remote query - used by combo/forms at present.
10244 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10247 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10250 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10251 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10254 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10255 * on any HTTP request
10258 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10261 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10265 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10266 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10268 remoteSort : false,
10271 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10272 * loaded or when a record is removed. (defaults to false).
10274 pruneModifiedRecords : false,
10277 lastOptions : null,
10280 * Add Records to the Store and fires the add event.
10281 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10283 add : function(records){
10284 records = [].concat(records);
10285 for(var i = 0, len = records.length; i < len; i++){
10286 records[i].join(this);
10288 var index = this.data.length;
10289 this.data.addAll(records);
10290 this.fireEvent("add", this, records, index);
10294 * Remove a Record from the Store and fires the remove event.
10295 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10297 remove : function(record){
10298 var index = this.data.indexOf(record);
10299 this.data.removeAt(index);
10300 if(this.pruneModifiedRecords){
10301 this.modified.remove(record);
10303 this.fireEvent("remove", this, record, index);
10307 * Remove all Records from the Store and fires the clear event.
10309 removeAll : function(){
10311 if(this.pruneModifiedRecords){
10312 this.modified = [];
10314 this.fireEvent("clear", this);
10318 * Inserts Records to the Store at the given index and fires the add event.
10319 * @param {Number} index The start index at which to insert the passed Records.
10320 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10322 insert : function(index, records){
10323 records = [].concat(records);
10324 for(var i = 0, len = records.length; i < len; i++){
10325 this.data.insert(index, records[i]);
10326 records[i].join(this);
10328 this.fireEvent("add", this, records, index);
10332 * Get the index within the cache of the passed Record.
10333 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10334 * @return {Number} The index of the passed Record. Returns -1 if not found.
10336 indexOf : function(record){
10337 return this.data.indexOf(record);
10341 * Get the index within the cache of the Record with the passed id.
10342 * @param {String} id The id of the Record to find.
10343 * @return {Number} The index of the Record. Returns -1 if not found.
10345 indexOfId : function(id){
10346 return this.data.indexOfKey(id);
10350 * Get the Record with the specified id.
10351 * @param {String} id The id of the Record to find.
10352 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10354 getById : function(id){
10355 return this.data.key(id);
10359 * Get the Record at the specified index.
10360 * @param {Number} index The index of the Record to find.
10361 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10363 getAt : function(index){
10364 return this.data.itemAt(index);
10368 * Returns a range of Records between specified indices.
10369 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10370 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10371 * @return {Roo.data.Record[]} An array of Records
10373 getRange : function(start, end){
10374 return this.data.getRange(start, end);
10378 storeOptions : function(o){
10379 o = Roo.apply({}, o);
10382 this.lastOptions = o;
10386 * Loads the Record cache from the configured Proxy using the configured Reader.
10388 * If using remote paging, then the first load call must specify the <em>start</em>
10389 * and <em>limit</em> properties in the options.params property to establish the initial
10390 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10392 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10393 * and this call will return before the new data has been loaded. Perform any post-processing
10394 * in a callback function, or in a "load" event handler.</strong>
10396 * @param {Object} options An object containing properties which control loading options:<ul>
10397 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10398 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10399 * passed the following arguments:<ul>
10400 * <li>r : Roo.data.Record[]</li>
10401 * <li>options: Options object from the load call</li>
10402 * <li>success: Boolean success indicator</li></ul></li>
10403 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10404 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10407 load : function(options){
10408 options = options || {};
10409 if(this.fireEvent("beforeload", this, options) !== false){
10410 this.storeOptions(options);
10411 var p = Roo.apply(options.params || {}, this.baseParams);
10412 // if meta was not loaded from remote source.. try requesting it.
10413 if (!this.reader.metaFromRemote) {
10414 p._requestMeta = 1;
10416 if(this.sortInfo && this.remoteSort){
10417 var pn = this.paramNames;
10418 p[pn["sort"]] = this.sortInfo.field;
10419 p[pn["dir"]] = this.sortInfo.direction;
10421 if (this.multiSort) {
10422 var pn = this.paramNames;
10423 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10426 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10431 * Reloads the Record cache from the configured Proxy using the configured Reader and
10432 * the options from the last load operation performed.
10433 * @param {Object} options (optional) An object containing properties which may override the options
10434 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10435 * the most recently used options are reused).
10437 reload : function(options){
10438 this.load(Roo.applyIf(options||{}, this.lastOptions));
10442 // Called as a callback by the Reader during a load operation.
10443 loadRecords : function(o, options, success){
10444 if(!o || success === false){
10445 if(success !== false){
10446 this.fireEvent("load", this, [], options, o);
10448 if(options.callback){
10449 options.callback.call(options.scope || this, [], options, false);
10453 // if data returned failure - throw an exception.
10454 if (o.success === false) {
10455 // show a message if no listener is registered.
10456 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10457 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10459 // loadmask wil be hooked into this..
10460 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10463 var r = o.records, t = o.totalRecords || r.length;
10465 this.fireEvent("beforeloadadd", this, r, options, o);
10467 if(!options || options.add !== true){
10468 if(this.pruneModifiedRecords){
10469 this.modified = [];
10471 for(var i = 0, len = r.length; i < len; i++){
10475 this.data = this.snapshot;
10476 delete this.snapshot;
10479 this.data.addAll(r);
10480 this.totalLength = t;
10482 this.fireEvent("datachanged", this);
10484 this.totalLength = Math.max(t, this.data.length+r.length);
10487 this.fireEvent("load", this, r, options, o);
10488 if(options.callback){
10489 options.callback.call(options.scope || this, r, options, true);
10495 * Loads data from a passed data block. A Reader which understands the format of the data
10496 * must have been configured in the constructor.
10497 * @param {Object} data The data block from which to read the Records. The format of the data expected
10498 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10499 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10501 loadData : function(o, append){
10502 var r = this.reader.readRecords(o);
10503 this.loadRecords(r, {add: append}, true);
10507 * Gets the number of cached records.
10509 * <em>If using paging, this may not be the total size of the dataset. If the data object
10510 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10511 * the data set size</em>
10513 getCount : function(){
10514 return this.data.length || 0;
10518 * Gets the total number of records in the dataset as returned by the server.
10520 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10521 * the dataset size</em>
10523 getTotalCount : function(){
10524 return this.totalLength || 0;
10528 * Returns the sort state of the Store as an object with two properties:
10530 field {String} The name of the field by which the Records are sorted
10531 direction {String} The sort order, "ASC" or "DESC"
10534 getSortState : function(){
10535 return this.sortInfo;
10539 applySort : function(){
10540 if(this.sortInfo && !this.remoteSort){
10541 var s = this.sortInfo, f = s.field;
10542 var st = this.fields.get(f).sortType;
10543 var fn = function(r1, r2){
10544 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10545 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10547 this.data.sort(s.direction, fn);
10548 if(this.snapshot && this.snapshot != this.data){
10549 this.snapshot.sort(s.direction, fn);
10555 * Sets the default sort column and order to be used by the next load operation.
10556 * @param {String} fieldName The name of the field to sort by.
10557 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10559 setDefaultSort : function(field, dir){
10560 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10564 * Sort the Records.
10565 * If remote sorting is used, the sort is performed on the server, and the cache is
10566 * reloaded. If local sorting is used, the cache is sorted internally.
10567 * @param {String} fieldName The name of the field to sort by.
10568 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10570 sort : function(fieldName, dir){
10571 var f = this.fields.get(fieldName);
10573 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10575 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10576 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10581 this.sortToggle[f.name] = dir;
10582 this.sortInfo = {field: f.name, direction: dir};
10583 if(!this.remoteSort){
10585 this.fireEvent("datachanged", this);
10587 this.load(this.lastOptions);
10592 * Calls the specified function for each of the Records in the cache.
10593 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10594 * Returning <em>false</em> aborts and exits the iteration.
10595 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10597 each : function(fn, scope){
10598 this.data.each(fn, scope);
10602 * Gets all records modified since the last commit. Modified records are persisted across load operations
10603 * (e.g., during paging).
10604 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10606 getModifiedRecords : function(){
10607 return this.modified;
10611 createFilterFn : function(property, value, anyMatch){
10612 if(!value.exec){ // not a regex
10613 value = String(value);
10614 if(value.length == 0){
10617 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10619 return function(r){
10620 return value.test(r.data[property]);
10625 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10626 * @param {String} property A field on your records
10627 * @param {Number} start The record index to start at (defaults to 0)
10628 * @param {Number} end The last record index to include (defaults to length - 1)
10629 * @return {Number} The sum
10631 sum : function(property, start, end){
10632 var rs = this.data.items, v = 0;
10633 start = start || 0;
10634 end = (end || end === 0) ? end : rs.length-1;
10636 for(var i = start; i <= end; i++){
10637 v += (rs[i].data[property] || 0);
10643 * Filter the records by a specified property.
10644 * @param {String} field A field on your records
10645 * @param {String/RegExp} value Either a string that the field
10646 * should start with or a RegExp to test against the field
10647 * @param {Boolean} anyMatch True to match any part not just the beginning
10649 filter : function(property, value, anyMatch){
10650 var fn = this.createFilterFn(property, value, anyMatch);
10651 return fn ? this.filterBy(fn) : this.clearFilter();
10655 * Filter by a function. The specified function will be called with each
10656 * record in this data source. If the function returns true the record is included,
10657 * otherwise it is filtered.
10658 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10659 * @param {Object} scope (optional) The scope of the function (defaults to this)
10661 filterBy : function(fn, scope){
10662 this.snapshot = this.snapshot || this.data;
10663 this.data = this.queryBy(fn, scope||this);
10664 this.fireEvent("datachanged", this);
10668 * Query the records by a specified property.
10669 * @param {String} field A field on your records
10670 * @param {String/RegExp} value Either a string that the field
10671 * should start with or a RegExp to test against the field
10672 * @param {Boolean} anyMatch True to match any part not just the beginning
10673 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10675 query : function(property, value, anyMatch){
10676 var fn = this.createFilterFn(property, value, anyMatch);
10677 return fn ? this.queryBy(fn) : this.data.clone();
10681 * Query by a function. The specified function will be called with each
10682 * record in this data source. If the function returns true the record is included
10684 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10685 * @param {Object} scope (optional) The scope of the function (defaults to this)
10686 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10688 queryBy : function(fn, scope){
10689 var data = this.snapshot || this.data;
10690 return data.filterBy(fn, scope||this);
10694 * Collects unique values for a particular dataIndex from this store.
10695 * @param {String} dataIndex The property to collect
10696 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10697 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10698 * @return {Array} An array of the unique values
10700 collect : function(dataIndex, allowNull, bypassFilter){
10701 var d = (bypassFilter === true && this.snapshot) ?
10702 this.snapshot.items : this.data.items;
10703 var v, sv, r = [], l = {};
10704 for(var i = 0, len = d.length; i < len; i++){
10705 v = d[i].data[dataIndex];
10707 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10716 * Revert to a view of the Record cache with no filtering applied.
10717 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10719 clearFilter : function(suppressEvent){
10720 if(this.snapshot && this.snapshot != this.data){
10721 this.data = this.snapshot;
10722 delete this.snapshot;
10723 if(suppressEvent !== true){
10724 this.fireEvent("datachanged", this);
10730 afterEdit : function(record){
10731 if(this.modified.indexOf(record) == -1){
10732 this.modified.push(record);
10734 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10738 afterReject : function(record){
10739 this.modified.remove(record);
10740 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10744 afterCommit : function(record){
10745 this.modified.remove(record);
10746 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10750 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10751 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10753 commitChanges : function(){
10754 var m = this.modified.slice(0);
10755 this.modified = [];
10756 for(var i = 0, len = m.length; i < len; i++){
10762 * Cancel outstanding changes on all changed records.
10764 rejectChanges : function(){
10765 var m = this.modified.slice(0);
10766 this.modified = [];
10767 for(var i = 0, len = m.length; i < len; i++){
10772 onMetaChange : function(meta, rtype, o){
10773 this.recordType = rtype;
10774 this.fields = rtype.prototype.fields;
10775 delete this.snapshot;
10776 this.sortInfo = meta.sortInfo || this.sortInfo;
10777 this.modified = [];
10778 this.fireEvent('metachange', this, this.reader.meta);
10781 moveIndex : function(data, type)
10783 var index = this.indexOf(data);
10785 var newIndex = index + type;
10789 this.insert(newIndex, data);
10794 * Ext JS Library 1.1.1
10795 * Copyright(c) 2006-2007, Ext JS, LLC.
10797 * Originally Released Under LGPL - original licence link has changed is not relivant.
10800 * <script type="text/javascript">
10804 * @class Roo.data.SimpleStore
10805 * @extends Roo.data.Store
10806 * Small helper class to make creating Stores from Array data easier.
10807 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10808 * @cfg {Array} fields An array of field definition objects, or field name strings.
10809 * @cfg {Array} data The multi-dimensional array of data
10811 * @param {Object} config
10813 Roo.data.SimpleStore = function(config){
10814 Roo.data.SimpleStore.superclass.constructor.call(this, {
10816 reader: new Roo.data.ArrayReader({
10819 Roo.data.Record.create(config.fields)
10821 proxy : new Roo.data.MemoryProxy(config.data)
10825 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10827 * Ext JS Library 1.1.1
10828 * Copyright(c) 2006-2007, Ext JS, LLC.
10830 * Originally Released Under LGPL - original licence link has changed is not relivant.
10833 * <script type="text/javascript">
10838 * @extends Roo.data.Store
10839 * @class Roo.data.JsonStore
10840 * Small helper class to make creating Stores for JSON data easier. <br/>
10842 var store = new Roo.data.JsonStore({
10843 url: 'get-images.php',
10845 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10848 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10849 * JsonReader and HttpProxy (unless inline data is provided).</b>
10850 * @cfg {Array} fields An array of field definition objects, or field name strings.
10852 * @param {Object} config
10854 Roo.data.JsonStore = function(c){
10855 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10856 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10857 reader: new Roo.data.JsonReader(c, c.fields)
10860 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10862 * Ext JS Library 1.1.1
10863 * Copyright(c) 2006-2007, Ext JS, LLC.
10865 * Originally Released Under LGPL - original licence link has changed is not relivant.
10868 * <script type="text/javascript">
10872 Roo.data.Field = function(config){
10873 if(typeof config == "string"){
10874 config = {name: config};
10876 Roo.apply(this, config);
10879 this.type = "auto";
10882 var st = Roo.data.SortTypes;
10883 // named sortTypes are supported, here we look them up
10884 if(typeof this.sortType == "string"){
10885 this.sortType = st[this.sortType];
10888 // set default sortType for strings and dates
10889 if(!this.sortType){
10892 this.sortType = st.asUCString;
10895 this.sortType = st.asDate;
10898 this.sortType = st.none;
10903 var stripRe = /[\$,%]/g;
10905 // prebuilt conversion function for this field, instead of
10906 // switching every time we're reading a value
10908 var cv, dateFormat = this.dateFormat;
10913 cv = function(v){ return v; };
10916 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10920 return v !== undefined && v !== null && v !== '' ?
10921 parseInt(String(v).replace(stripRe, ""), 10) : '';
10926 return v !== undefined && v !== null && v !== '' ?
10927 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10932 cv = function(v){ return v === true || v === "true" || v == 1; };
10939 if(v instanceof Date){
10943 if(dateFormat == "timestamp"){
10944 return new Date(v*1000);
10946 return Date.parseDate(v, dateFormat);
10948 var parsed = Date.parse(v);
10949 return parsed ? new Date(parsed) : null;
10958 Roo.data.Field.prototype = {
10966 * Ext JS Library 1.1.1
10967 * Copyright(c) 2006-2007, Ext JS, LLC.
10969 * Originally Released Under LGPL - original licence link has changed is not relivant.
10972 * <script type="text/javascript">
10975 // Base class for reading structured data from a data source. This class is intended to be
10976 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10979 * @class Roo.data.DataReader
10980 * Base class for reading structured data from a data source. This class is intended to be
10981 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10984 Roo.data.DataReader = function(meta, recordType){
10988 this.recordType = recordType instanceof Array ?
10989 Roo.data.Record.create(recordType) : recordType;
10992 Roo.data.DataReader.prototype = {
10994 * Create an empty record
10995 * @param {Object} data (optional) - overlay some values
10996 * @return {Roo.data.Record} record created.
10998 newRow : function(d) {
11000 this.recordType.prototype.fields.each(function(c) {
11002 case 'int' : da[c.name] = 0; break;
11003 case 'date' : da[c.name] = new Date(); break;
11004 case 'float' : da[c.name] = 0.0; break;
11005 case 'boolean' : da[c.name] = false; break;
11006 default : da[c.name] = ""; break;
11010 return new this.recordType(Roo.apply(da, d));
11015 * Ext JS Library 1.1.1
11016 * Copyright(c) 2006-2007, Ext JS, LLC.
11018 * Originally Released Under LGPL - original licence link has changed is not relivant.
11021 * <script type="text/javascript">
11025 * @class Roo.data.DataProxy
11026 * @extends Roo.data.Observable
11027 * This class is an abstract base class for implementations which provide retrieval of
11028 * unformatted data objects.<br>
11030 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11031 * (of the appropriate type which knows how to parse the data object) to provide a block of
11032 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11034 * Custom implementations must implement the load method as described in
11035 * {@link Roo.data.HttpProxy#load}.
11037 Roo.data.DataProxy = function(){
11040 * @event beforeload
11041 * Fires before a network request is made to retrieve a data object.
11042 * @param {Object} This DataProxy object.
11043 * @param {Object} params The params parameter to the load function.
11048 * Fires before the load method's callback is called.
11049 * @param {Object} This DataProxy object.
11050 * @param {Object} o The data object.
11051 * @param {Object} arg The callback argument object passed to the load function.
11055 * @event loadexception
11056 * Fires if an Exception occurs during data retrieval.
11057 * @param {Object} This DataProxy object.
11058 * @param {Object} o The data object.
11059 * @param {Object} arg The callback argument object passed to the load function.
11060 * @param {Object} e The Exception.
11062 loadexception : true
11064 Roo.data.DataProxy.superclass.constructor.call(this);
11067 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11070 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11074 * Ext JS Library 1.1.1
11075 * Copyright(c) 2006-2007, Ext JS, LLC.
11077 * Originally Released Under LGPL - original licence link has changed is not relivant.
11080 * <script type="text/javascript">
11083 * @class Roo.data.MemoryProxy
11084 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11085 * to the Reader when its load method is called.
11087 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11089 Roo.data.MemoryProxy = function(data){
11093 Roo.data.MemoryProxy.superclass.constructor.call(this);
11097 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11100 * Load data from the requested source (in this case an in-memory
11101 * data object passed to the constructor), read the data object into
11102 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11103 * process that block using the passed callback.
11104 * @param {Object} params This parameter is not used by the MemoryProxy class.
11105 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11106 * object into a block of Roo.data.Records.
11107 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11108 * The function must be passed <ul>
11109 * <li>The Record block object</li>
11110 * <li>The "arg" argument from the load function</li>
11111 * <li>A boolean success indicator</li>
11113 * @param {Object} scope The scope in which to call the callback
11114 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11116 load : function(params, reader, callback, scope, arg){
11117 params = params || {};
11120 result = reader.readRecords(this.data);
11122 this.fireEvent("loadexception", this, arg, null, e);
11123 callback.call(scope, null, arg, false);
11126 callback.call(scope, result, arg, true);
11130 update : function(params, records){
11135 * Ext JS Library 1.1.1
11136 * Copyright(c) 2006-2007, Ext JS, LLC.
11138 * Originally Released Under LGPL - original licence link has changed is not relivant.
11141 * <script type="text/javascript">
11144 * @class Roo.data.HttpProxy
11145 * @extends Roo.data.DataProxy
11146 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11147 * configured to reference a certain URL.<br><br>
11149 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11150 * from which the running page was served.<br><br>
11152 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11154 * Be aware that to enable the browser to parse an XML document, the server must set
11155 * the Content-Type header in the HTTP response to "text/xml".
11157 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11158 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11159 * will be used to make the request.
11161 Roo.data.HttpProxy = function(conn){
11162 Roo.data.HttpProxy.superclass.constructor.call(this);
11163 // is conn a conn config or a real conn?
11165 this.useAjax = !conn || !conn.events;
11169 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11170 // thse are take from connection...
11173 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11176 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11177 * extra parameters to each request made by this object. (defaults to undefined)
11180 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11181 * to each request made by this object. (defaults to undefined)
11184 * @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)
11187 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11190 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11196 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11200 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11201 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11202 * a finer-grained basis than the DataProxy events.
11204 getConnection : function(){
11205 return this.useAjax ? Roo.Ajax : this.conn;
11209 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11210 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11211 * process that block using the passed callback.
11212 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11213 * for the request to the remote server.
11214 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11215 * object into a block of Roo.data.Records.
11216 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11217 * The function must be passed <ul>
11218 * <li>The Record block object</li>
11219 * <li>The "arg" argument from the load function</li>
11220 * <li>A boolean success indicator</li>
11222 * @param {Object} scope The scope in which to call the callback
11223 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11225 load : function(params, reader, callback, scope, arg){
11226 if(this.fireEvent("beforeload", this, params) !== false){
11228 params : params || {},
11230 callback : callback,
11235 callback : this.loadResponse,
11239 Roo.applyIf(o, this.conn);
11240 if(this.activeRequest){
11241 Roo.Ajax.abort(this.activeRequest);
11243 this.activeRequest = Roo.Ajax.request(o);
11245 this.conn.request(o);
11248 callback.call(scope||this, null, arg, false);
11253 loadResponse : function(o, success, response){
11254 delete this.activeRequest;
11256 this.fireEvent("loadexception", this, o, response);
11257 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11262 result = o.reader.read(response);
11264 this.fireEvent("loadexception", this, o, response, e);
11265 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11269 this.fireEvent("load", this, o, o.request.arg);
11270 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11274 update : function(dataSet){
11279 updateResponse : function(dataSet){
11284 * Ext JS Library 1.1.1
11285 * Copyright(c) 2006-2007, Ext JS, LLC.
11287 * Originally Released Under LGPL - original licence link has changed is not relivant.
11290 * <script type="text/javascript">
11294 * @class Roo.data.ScriptTagProxy
11295 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11296 * other than the originating domain of the running page.<br><br>
11298 * <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
11299 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11301 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11302 * source code that is used as the source inside a <script> tag.<br><br>
11304 * In order for the browser to process the returned data, the server must wrap the data object
11305 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11306 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11307 * depending on whether the callback name was passed:
11310 boolean scriptTag = false;
11311 String cb = request.getParameter("callback");
11314 response.setContentType("text/javascript");
11316 response.setContentType("application/x-json");
11318 Writer out = response.getWriter();
11320 out.write(cb + "(");
11322 out.print(dataBlock.toJsonString());
11329 * @param {Object} config A configuration object.
11331 Roo.data.ScriptTagProxy = function(config){
11332 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11333 Roo.apply(this, config);
11334 this.head = document.getElementsByTagName("head")[0];
11337 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11339 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11341 * @cfg {String} url The URL from which to request the data object.
11344 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11348 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11349 * the server the name of the callback function set up by the load call to process the returned data object.
11350 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11351 * javascript output which calls this named function passing the data object as its only parameter.
11353 callbackParam : "callback",
11355 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11356 * name to the request.
11361 * Load data from the configured URL, read the data object into
11362 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11363 * process that block using the passed callback.
11364 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11365 * for the request to the remote server.
11366 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11367 * object into a block of Roo.data.Records.
11368 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11369 * The function must be passed <ul>
11370 * <li>The Record block object</li>
11371 * <li>The "arg" argument from the load function</li>
11372 * <li>A boolean success indicator</li>
11374 * @param {Object} scope The scope in which to call the callback
11375 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11377 load : function(params, reader, callback, scope, arg){
11378 if(this.fireEvent("beforeload", this, params) !== false){
11380 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11382 var url = this.url;
11383 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11385 url += "&_dc=" + (new Date().getTime());
11387 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11390 cb : "stcCallback"+transId,
11391 scriptId : "stcScript"+transId,
11395 callback : callback,
11401 window[trans.cb] = function(o){
11402 conn.handleResponse(o, trans);
11405 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11407 if(this.autoAbort !== false){
11411 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11413 var script = document.createElement("script");
11414 script.setAttribute("src", url);
11415 script.setAttribute("type", "text/javascript");
11416 script.setAttribute("id", trans.scriptId);
11417 this.head.appendChild(script);
11419 this.trans = trans;
11421 callback.call(scope||this, null, arg, false);
11426 isLoading : function(){
11427 return this.trans ? true : false;
11431 * Abort the current server request.
11433 abort : function(){
11434 if(this.isLoading()){
11435 this.destroyTrans(this.trans);
11440 destroyTrans : function(trans, isLoaded){
11441 this.head.removeChild(document.getElementById(trans.scriptId));
11442 clearTimeout(trans.timeoutId);
11444 window[trans.cb] = undefined;
11446 delete window[trans.cb];
11449 // if hasn't been loaded, wait for load to remove it to prevent script error
11450 window[trans.cb] = function(){
11451 window[trans.cb] = undefined;
11453 delete window[trans.cb];
11460 handleResponse : function(o, trans){
11461 this.trans = false;
11462 this.destroyTrans(trans, true);
11465 result = trans.reader.readRecords(o);
11467 this.fireEvent("loadexception", this, o, trans.arg, e);
11468 trans.callback.call(trans.scope||window, null, trans.arg, false);
11471 this.fireEvent("load", this, o, trans.arg);
11472 trans.callback.call(trans.scope||window, result, trans.arg, true);
11476 handleFailure : function(trans){
11477 this.trans = false;
11478 this.destroyTrans(trans, false);
11479 this.fireEvent("loadexception", this, null, trans.arg);
11480 trans.callback.call(trans.scope||window, null, trans.arg, false);
11484 * Ext JS Library 1.1.1
11485 * Copyright(c) 2006-2007, Ext JS, LLC.
11487 * Originally Released Under LGPL - original licence link has changed is not relivant.
11490 * <script type="text/javascript">
11494 * @class Roo.data.JsonReader
11495 * @extends Roo.data.DataReader
11496 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11497 * based on mappings in a provided Roo.data.Record constructor.
11499 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11500 * in the reply previously.
11505 var RecordDef = Roo.data.Record.create([
11506 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11507 {name: 'occupation'} // This field will use "occupation" as the mapping.
11509 var myReader = new Roo.data.JsonReader({
11510 totalProperty: "results", // The property which contains the total dataset size (optional)
11511 root: "rows", // The property which contains an Array of row objects
11512 id: "id" // The property within each row object that provides an ID for the record (optional)
11516 * This would consume a JSON file like this:
11518 { 'results': 2, 'rows': [
11519 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11520 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11523 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11524 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11525 * paged from the remote server.
11526 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11527 * @cfg {String} root name of the property which contains the Array of row objects.
11528 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11529 * @cfg {Array} fields Array of field definition objects
11531 * Create a new JsonReader
11532 * @param {Object} meta Metadata configuration options
11533 * @param {Object} recordType Either an Array of field definition objects,
11534 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11536 Roo.data.JsonReader = function(meta, recordType){
11539 // set some defaults:
11540 Roo.applyIf(meta, {
11541 totalProperty: 'total',
11542 successProperty : 'success',
11547 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11549 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11552 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11553 * Used by Store query builder to append _requestMeta to params.
11556 metaFromRemote : false,
11558 * This method is only used by a DataProxy which has retrieved data from a remote server.
11559 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11560 * @return {Object} data A data block which is used by an Roo.data.Store object as
11561 * a cache of Roo.data.Records.
11563 read : function(response){
11564 var json = response.responseText;
11566 var o = /* eval:var:o */ eval("("+json+")");
11568 throw {message: "JsonReader.read: Json object not found"};
11574 this.metaFromRemote = true;
11575 this.meta = o.metaData;
11576 this.recordType = Roo.data.Record.create(o.metaData.fields);
11577 this.onMetaChange(this.meta, this.recordType, o);
11579 return this.readRecords(o);
11582 // private function a store will implement
11583 onMetaChange : function(meta, recordType, o){
11590 simpleAccess: function(obj, subsc) {
11597 getJsonAccessor: function(){
11599 return function(expr) {
11601 return(re.test(expr))
11602 ? new Function("obj", "return obj." + expr)
11607 return Roo.emptyFn;
11612 * Create a data block containing Roo.data.Records from an XML document.
11613 * @param {Object} o An object which contains an Array of row objects in the property specified
11614 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11615 * which contains the total size of the dataset.
11616 * @return {Object} data A data block which is used by an Roo.data.Store object as
11617 * a cache of Roo.data.Records.
11619 readRecords : function(o){
11621 * After any data loads, the raw JSON data is available for further custom processing.
11625 var s = this.meta, Record = this.recordType,
11626 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11628 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11630 if(s.totalProperty) {
11631 this.getTotal = this.getJsonAccessor(s.totalProperty);
11633 if(s.successProperty) {
11634 this.getSuccess = this.getJsonAccessor(s.successProperty);
11636 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11638 var g = this.getJsonAccessor(s.id);
11639 this.getId = function(rec) {
11641 return (r === undefined || r === "") ? null : r;
11644 this.getId = function(){return null;};
11647 for(var jj = 0; jj < fl; jj++){
11649 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11650 this.ef[jj] = this.getJsonAccessor(map);
11654 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11655 if(s.totalProperty){
11656 var vt = parseInt(this.getTotal(o), 10);
11661 if(s.successProperty){
11662 var vs = this.getSuccess(o);
11663 if(vs === false || vs === 'false'){
11668 for(var i = 0; i < c; i++){
11671 var id = this.getId(n);
11672 for(var j = 0; j < fl; j++){
11674 var v = this.ef[j](n);
11676 Roo.log('missing convert for ' + f.name);
11680 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11682 var record = new Record(values, id);
11684 records[i] = record;
11690 totalRecords : totalRecords
11695 * Ext JS Library 1.1.1
11696 * Copyright(c) 2006-2007, Ext JS, LLC.
11698 * Originally Released Under LGPL - original licence link has changed is not relivant.
11701 * <script type="text/javascript">
11705 * @class Roo.data.ArrayReader
11706 * @extends Roo.data.DataReader
11707 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11708 * Each element of that Array represents a row of data fields. The
11709 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11710 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11714 var RecordDef = Roo.data.Record.create([
11715 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11716 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11718 var myReader = new Roo.data.ArrayReader({
11719 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11723 * This would consume an Array like this:
11725 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11727 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11729 * Create a new JsonReader
11730 * @param {Object} meta Metadata configuration options.
11731 * @param {Object} recordType Either an Array of field definition objects
11732 * as specified to {@link Roo.data.Record#create},
11733 * or an {@link Roo.data.Record} object
11734 * created using {@link Roo.data.Record#create}.
11736 Roo.data.ArrayReader = function(meta, recordType){
11737 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11740 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11742 * Create a data block containing Roo.data.Records from an XML document.
11743 * @param {Object} o An Array of row objects which represents the dataset.
11744 * @return {Object} data A data block which is used by an Roo.data.Store object as
11745 * a cache of Roo.data.Records.
11747 readRecords : function(o){
11748 var sid = this.meta ? this.meta.id : null;
11749 var recordType = this.recordType, fields = recordType.prototype.fields;
11752 for(var i = 0; i < root.length; i++){
11755 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11756 for(var j = 0, jlen = fields.length; j < jlen; j++){
11757 var f = fields.items[j];
11758 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11759 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11761 values[f.name] = v;
11763 var record = new recordType(values, id);
11765 records[records.length] = record;
11769 totalRecords : records.length
11778 * @class Roo.bootstrap.ComboBox
11779 * @extends Roo.bootstrap.TriggerField
11780 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11781 * @cfg {Boolean} append (true|false) default false
11782 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11783 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11784 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11785 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11786 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11787 * @cfg {Boolean} animate default true
11788 * @cfg {Boolean} emptyResultText only for touch device
11789 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11791 * Create a new ComboBox.
11792 * @param {Object} config Configuration options
11794 Roo.bootstrap.ComboBox = function(config){
11795 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11799 * Fires when the dropdown list is expanded
11800 * @param {Roo.bootstrap.ComboBox} combo This combo box
11805 * Fires when the dropdown list is collapsed
11806 * @param {Roo.bootstrap.ComboBox} combo This combo box
11810 * @event beforeselect
11811 * Fires before a list item is selected. Return false to cancel the selection.
11812 * @param {Roo.bootstrap.ComboBox} combo This combo box
11813 * @param {Roo.data.Record} record The data record returned from the underlying store
11814 * @param {Number} index The index of the selected item in the dropdown list
11816 'beforeselect' : true,
11819 * Fires when a list item is selected
11820 * @param {Roo.bootstrap.ComboBox} combo This combo box
11821 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11822 * @param {Number} index The index of the selected item in the dropdown list
11826 * @event beforequery
11827 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11828 * The event object passed has these properties:
11829 * @param {Roo.bootstrap.ComboBox} combo This combo box
11830 * @param {String} query The query
11831 * @param {Boolean} forceAll true to force "all" query
11832 * @param {Boolean} cancel true to cancel the query
11833 * @param {Object} e The query event object
11835 'beforequery': true,
11838 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11839 * @param {Roo.bootstrap.ComboBox} combo This combo box
11844 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11845 * @param {Roo.bootstrap.ComboBox} combo This combo box
11846 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11851 * Fires when the remove value from the combobox array
11852 * @param {Roo.bootstrap.ComboBox} combo This combo box
11856 * @event afterremove
11857 * Fires when the remove value from the combobox array
11858 * @param {Roo.bootstrap.ComboBox} combo This combo box
11860 'afterremove' : true,
11862 * @event specialfilter
11863 * Fires when specialfilter
11864 * @param {Roo.bootstrap.ComboBox} combo This combo box
11866 'specialfilter' : true,
11869 * Fires when tick the element
11870 * @param {Roo.bootstrap.ComboBox} combo This combo box
11874 * @event touchviewdisplay
11875 * Fires when touch view require special display (default is using displayField)
11876 * @param {Roo.bootstrap.ComboBox} combo This combo box
11877 * @param {Object} cfg set html .
11879 'touchviewdisplay' : true
11884 this.tickItems = [];
11886 this.selectedIndex = -1;
11887 if(this.mode == 'local'){
11888 if(config.queryDelay === undefined){
11889 this.queryDelay = 10;
11891 if(config.minChars === undefined){
11897 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11900 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11901 * rendering into an Roo.Editor, defaults to false)
11904 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11905 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11908 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11911 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11912 * the dropdown list (defaults to undefined, with no header element)
11916 * @cfg {String/Roo.Template} tpl The template to use to render the output
11920 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11922 listWidth: undefined,
11924 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11925 * mode = 'remote' or 'text' if mode = 'local')
11927 displayField: undefined,
11930 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11931 * mode = 'remote' or 'value' if mode = 'local').
11932 * Note: use of a valueField requires the user make a selection
11933 * in order for a value to be mapped.
11935 valueField: undefined,
11939 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11940 * field's data value (defaults to the underlying DOM element's name)
11942 hiddenName: undefined,
11944 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11948 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11950 selectedClass: 'active',
11953 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11957 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11958 * anchor positions (defaults to 'tl-bl')
11960 listAlign: 'tl-bl?',
11962 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11966 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11967 * query specified by the allQuery config option (defaults to 'query')
11969 triggerAction: 'query',
11971 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11972 * (defaults to 4, does not apply if editable = false)
11976 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11977 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11981 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11982 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11986 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11987 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11991 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11992 * when editable = true (defaults to false)
11994 selectOnFocus:false,
11996 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11998 queryParam: 'query',
12000 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12001 * when mode = 'remote' (defaults to 'Loading...')
12003 loadingText: 'Loading...',
12005 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12009 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12013 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12014 * traditional select (defaults to true)
12018 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12022 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12026 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12027 * listWidth has a higher value)
12031 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12032 * allow the user to set arbitrary text into the field (defaults to false)
12034 forceSelection:false,
12036 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12037 * if typeAhead = true (defaults to 250)
12039 typeAheadDelay : 250,
12041 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12042 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12044 valueNotFoundText : undefined,
12046 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12048 blockFocus : false,
12051 * @cfg {Boolean} disableClear Disable showing of clear button.
12053 disableClear : false,
12055 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12057 alwaysQuery : false,
12060 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12065 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12067 invalidClass : "has-warning",
12070 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12072 validClass : "has-success",
12075 * @cfg {Boolean} specialFilter (true|false) special filter default false
12077 specialFilter : false,
12080 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12082 mobileTouchView : true,
12094 btnPosition : 'right',
12095 triggerList : true,
12096 showToggleBtn : true,
12098 emptyResultText: 'Empty',
12099 triggerText : 'Select',
12101 // element that contains real text value.. (when hidden is used..)
12103 getAutoCreate : function()
12111 if(Roo.isTouch && this.mobileTouchView){
12112 cfg = this.getAutoCreateTouchView();
12119 if(!this.tickable){
12120 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12125 * ComboBox with tickable selections
12128 var align = this.labelAlign || this.parentLabelAlign();
12131 cls : 'form-group roo-combobox-tickable' //input-group
12136 cls : 'tickable-buttons',
12141 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12142 html : this.triggerText
12148 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12155 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12162 buttons.cn.unshift({
12164 cls: 'roo-select2-search-field-input'
12170 Roo.each(buttons.cn, function(c){
12172 c.cls += ' btn-' + _this.size;
12175 if (_this.disabled) {
12186 cls: 'form-hidden-field'
12190 cls: 'roo-select2-choices',
12194 cls: 'roo-select2-search-field',
12206 cls: 'roo-select2-container input-group roo-select2-container-multi',
12211 // cls: 'typeahead typeahead-long dropdown-menu',
12212 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12217 if(this.hasFeedback && !this.allowBlank){
12221 cls: 'glyphicon form-control-feedback'
12224 combobox.cn.push(feedback);
12227 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12229 // Roo.log("left and has label");
12235 cls : 'control-label col-sm-' + this.labelWidth,
12236 html : this.fieldLabel
12240 cls : "col-sm-" + (12 - this.labelWidth),
12247 } else if ( this.fieldLabel.length) {
12248 // Roo.log(" label");
12253 //cls : 'input-group-addon',
12254 html : this.fieldLabel
12264 // Roo.log(" no label && no align");
12271 ['xs','sm','md','lg'].map(function(size){
12272 if (settings[size]) {
12273 cfg.cls += ' col-' + size + '-' + settings[size];
12281 _initEventsCalled : false,
12284 initEvents: function()
12287 if (this._initEventsCalled) { // as we call render... prevent looping...
12290 this._initEventsCalled = true;
12293 throw "can not find store for combo";
12296 this.store = Roo.factory(this.store, Roo.data);
12298 // if we are building from html. then this element is so complex, that we can not really
12299 // use the rendered HTML.
12300 // so we have to trash and replace the previous code.
12301 if (Roo.XComponent.build_from_html) {
12303 // remove this element....
12304 var e = this.el.dom, k=0;
12305 while (e ) { e = e.previousSibling; ++k;}
12310 this.rendered = false;
12312 this.render(this.parent().getChildContainer(true), k);
12323 if(Roo.isTouch && this.mobileTouchView){
12324 this.initTouchView();
12329 this.initTickableEvents();
12333 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12335 if(this.hiddenName){
12337 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12339 this.hiddenField.dom.value =
12340 this.hiddenValue !== undefined ? this.hiddenValue :
12341 this.value !== undefined ? this.value : '';
12343 // prevent input submission
12344 this.el.dom.removeAttribute('name');
12345 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12350 // this.el.dom.setAttribute('autocomplete', 'off');
12353 var cls = 'x-combo-list';
12355 //this.list = new Roo.Layer({
12356 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12362 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12363 _this.list.setWidth(lw);
12366 this.list.on('mouseover', this.onViewOver, this);
12367 this.list.on('mousemove', this.onViewMove, this);
12369 this.list.on('scroll', this.onViewScroll, this);
12372 this.list.swallowEvent('mousewheel');
12373 this.assetHeight = 0;
12376 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12377 this.assetHeight += this.header.getHeight();
12380 this.innerList = this.list.createChild({cls:cls+'-inner'});
12381 this.innerList.on('mouseover', this.onViewOver, this);
12382 this.innerList.on('mousemove', this.onViewMove, this);
12383 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12385 if(this.allowBlank && !this.pageSize && !this.disableClear){
12386 this.footer = this.list.createChild({cls:cls+'-ft'});
12387 this.pageTb = new Roo.Toolbar(this.footer);
12391 this.footer = this.list.createChild({cls:cls+'-ft'});
12392 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12393 {pageSize: this.pageSize});
12397 if (this.pageTb && this.allowBlank && !this.disableClear) {
12399 this.pageTb.add(new Roo.Toolbar.Fill(), {
12400 cls: 'x-btn-icon x-btn-clear',
12402 handler: function()
12405 _this.clearValue();
12406 _this.onSelect(false, -1);
12411 this.assetHeight += this.footer.getHeight();
12416 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12419 this.view = new Roo.View(this.list, this.tpl, {
12420 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12422 //this.view.wrapEl.setDisplayed(false);
12423 this.view.on('click', this.onViewClick, this);
12427 this.store.on('beforeload', this.onBeforeLoad, this);
12428 this.store.on('load', this.onLoad, this);
12429 this.store.on('loadexception', this.onLoadException, this);
12431 if(this.resizable){
12432 this.resizer = new Roo.Resizable(this.list, {
12433 pinned:true, handles:'se'
12435 this.resizer.on('resize', function(r, w, h){
12436 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12437 this.listWidth = w;
12438 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12439 this.restrictHeight();
12441 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12444 if(!this.editable){
12445 this.editable = true;
12446 this.setEditable(false);
12451 if (typeof(this.events.add.listeners) != 'undefined') {
12453 this.addicon = this.wrap.createChild(
12454 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12456 this.addicon.on('click', function(e) {
12457 this.fireEvent('add', this);
12460 if (typeof(this.events.edit.listeners) != 'undefined') {
12462 this.editicon = this.wrap.createChild(
12463 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12464 if (this.addicon) {
12465 this.editicon.setStyle('margin-left', '40px');
12467 this.editicon.on('click', function(e) {
12469 // we fire even if inothing is selected..
12470 this.fireEvent('edit', this, this.lastData );
12476 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12477 "up" : function(e){
12478 this.inKeyMode = true;
12482 "down" : function(e){
12483 if(!this.isExpanded()){
12484 this.onTriggerClick();
12486 this.inKeyMode = true;
12491 "enter" : function(e){
12492 // this.onViewClick();
12496 if(this.fireEvent("specialkey", this, e)){
12497 this.onViewClick(false);
12503 "esc" : function(e){
12507 "tab" : function(e){
12510 if(this.fireEvent("specialkey", this, e)){
12511 this.onViewClick(false);
12519 doRelay : function(foo, bar, hname){
12520 if(hname == 'down' || this.scope.isExpanded()){
12521 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12530 this.queryDelay = Math.max(this.queryDelay || 10,
12531 this.mode == 'local' ? 10 : 250);
12534 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12536 if(this.typeAhead){
12537 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12539 if(this.editable !== false){
12540 this.inputEl().on("keyup", this.onKeyUp, this);
12542 if(this.forceSelection){
12543 this.inputEl().on('blur', this.doForce, this);
12547 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12548 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12552 initTickableEvents: function()
12556 if(this.hiddenName){
12558 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12560 this.hiddenField.dom.value =
12561 this.hiddenValue !== undefined ? this.hiddenValue :
12562 this.value !== undefined ? this.value : '';
12564 // prevent input submission
12565 this.el.dom.removeAttribute('name');
12566 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12571 // this.list = this.el.select('ul.dropdown-menu',true).first();
12573 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12574 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12575 if(this.triggerList){
12576 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12579 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12580 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12582 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12583 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12585 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12586 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12588 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12589 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12590 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12593 this.cancelBtn.hide();
12598 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12599 _this.list.setWidth(lw);
12602 this.list.on('mouseover', this.onViewOver, this);
12603 this.list.on('mousemove', this.onViewMove, this);
12605 this.list.on('scroll', this.onViewScroll, this);
12608 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>';
12611 this.view = new Roo.View(this.list, this.tpl, {
12612 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12615 //this.view.wrapEl.setDisplayed(false);
12616 this.view.on('click', this.onViewClick, this);
12620 this.store.on('beforeload', this.onBeforeLoad, this);
12621 this.store.on('load', this.onLoad, this);
12622 this.store.on('loadexception', this.onLoadException, this);
12625 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12626 "up" : function(e){
12627 this.inKeyMode = true;
12631 "down" : function(e){
12632 this.inKeyMode = true;
12636 "enter" : function(e){
12637 if(this.fireEvent("specialkey", this, e)){
12638 this.onViewClick(false);
12644 "esc" : function(e){
12645 this.onTickableFooterButtonClick(e, false, false);
12648 "tab" : function(e){
12649 this.fireEvent("specialkey", this, e);
12651 this.onTickableFooterButtonClick(e, false, false);
12658 doRelay : function(e, fn, key){
12659 if(this.scope.isExpanded()){
12660 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12669 this.queryDelay = Math.max(this.queryDelay || 10,
12670 this.mode == 'local' ? 10 : 250);
12673 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12675 if(this.typeAhead){
12676 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12679 if(this.editable !== false){
12680 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12685 onDestroy : function(){
12687 this.view.setStore(null);
12688 this.view.el.removeAllListeners();
12689 this.view.el.remove();
12690 this.view.purgeListeners();
12693 this.list.dom.innerHTML = '';
12697 this.store.un('beforeload', this.onBeforeLoad, this);
12698 this.store.un('load', this.onLoad, this);
12699 this.store.un('loadexception', this.onLoadException, this);
12701 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12705 fireKey : function(e){
12706 if(e.isNavKeyPress() && !this.list.isVisible()){
12707 this.fireEvent("specialkey", this, e);
12712 onResize: function(w, h){
12713 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12715 // if(typeof w != 'number'){
12716 // // we do not handle it!?!?
12719 // var tw = this.trigger.getWidth();
12720 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12721 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12723 // this.inputEl().setWidth( this.adjustWidth('input', x));
12725 // //this.trigger.setStyle('left', x+'px');
12727 // if(this.list && this.listWidth === undefined){
12728 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12729 // this.list.setWidth(lw);
12730 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12738 * Allow or prevent the user from directly editing the field text. If false is passed,
12739 * the user will only be able to select from the items defined in the dropdown list. This method
12740 * is the runtime equivalent of setting the 'editable' config option at config time.
12741 * @param {Boolean} value True to allow the user to directly edit the field text
12743 setEditable : function(value){
12744 if(value == this.editable){
12747 this.editable = value;
12749 this.inputEl().dom.setAttribute('readOnly', true);
12750 this.inputEl().on('mousedown', this.onTriggerClick, this);
12751 this.inputEl().addClass('x-combo-noedit');
12753 this.inputEl().dom.setAttribute('readOnly', false);
12754 this.inputEl().un('mousedown', this.onTriggerClick, this);
12755 this.inputEl().removeClass('x-combo-noedit');
12761 onBeforeLoad : function(combo,opts){
12762 if(!this.hasFocus){
12766 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12768 this.restrictHeight();
12769 this.selectedIndex = -1;
12773 onLoad : function(){
12775 this.hasQuery = false;
12777 if(!this.hasFocus){
12781 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12782 this.loading.hide();
12785 if(this.store.getCount() > 0){
12787 this.restrictHeight();
12788 if(this.lastQuery == this.allQuery){
12789 if(this.editable && !this.tickable){
12790 this.inputEl().dom.select();
12794 !this.selectByValue(this.value, true) &&
12797 !this.store.lastOptions ||
12798 typeof(this.store.lastOptions.add) == 'undefined' ||
12799 this.store.lastOptions.add != true
12802 this.select(0, true);
12805 if(this.autoFocus){
12808 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12809 this.taTask.delay(this.typeAheadDelay);
12813 this.onEmptyResults();
12819 onLoadException : function()
12821 this.hasQuery = false;
12823 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12824 this.loading.hide();
12827 if(this.tickable && this.editable){
12832 // only causes errors at present
12833 //Roo.log(this.store.reader.jsonData);
12834 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12836 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12842 onTypeAhead : function(){
12843 if(this.store.getCount() > 0){
12844 var r = this.store.getAt(0);
12845 var newValue = r.data[this.displayField];
12846 var len = newValue.length;
12847 var selStart = this.getRawValue().length;
12849 if(selStart != len){
12850 this.setRawValue(newValue);
12851 this.selectText(selStart, newValue.length);
12857 onSelect : function(record, index){
12859 if(this.fireEvent('beforeselect', this, record, index) !== false){
12861 this.setFromData(index > -1 ? record.data : false);
12864 this.fireEvent('select', this, record, index);
12869 * Returns the currently selected field value or empty string if no value is set.
12870 * @return {String} value The selected value
12872 getValue : function(){
12875 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12878 if(this.valueField){
12879 return typeof this.value != 'undefined' ? this.value : '';
12881 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12886 * Clears any text/value currently set in the field
12888 clearValue : function(){
12889 if(this.hiddenField){
12890 this.hiddenField.dom.value = '';
12893 this.setRawValue('');
12894 this.lastSelectionText = '';
12895 this.lastData = false;
12897 var close = this.closeTriggerEl();
12906 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12907 * will be displayed in the field. If the value does not match the data value of an existing item,
12908 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12909 * Otherwise the field will be blank (although the value will still be set).
12910 * @param {String} value The value to match
12912 setValue : function(v){
12919 if(this.valueField){
12920 var r = this.findRecord(this.valueField, v);
12922 text = r.data[this.displayField];
12923 }else if(this.valueNotFoundText !== undefined){
12924 text = this.valueNotFoundText;
12927 this.lastSelectionText = text;
12928 if(this.hiddenField){
12929 this.hiddenField.dom.value = v;
12931 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12934 var close = this.closeTriggerEl();
12937 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12941 * @property {Object} the last set data for the element
12946 * Sets the value of the field based on a object which is related to the record format for the store.
12947 * @param {Object} value the value to set as. or false on reset?
12949 setFromData : function(o){
12956 var dv = ''; // display value
12957 var vv = ''; // value value..
12959 if (this.displayField) {
12960 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12962 // this is an error condition!!!
12963 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12966 if(this.valueField){
12967 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12970 var close = this.closeTriggerEl();
12973 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12976 if(this.hiddenField){
12977 this.hiddenField.dom.value = vv;
12979 this.lastSelectionText = dv;
12980 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12984 // no hidden field.. - we store the value in 'value', but still display
12985 // display field!!!!
12986 this.lastSelectionText = dv;
12987 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12994 reset : function(){
12995 // overridden so that last data is reset..
13002 this.setValue(this.originalValue);
13003 this.clearInvalid();
13004 this.lastData = false;
13006 this.view.clearSelections();
13010 findRecord : function(prop, value){
13012 if(this.store.getCount() > 0){
13013 this.store.each(function(r){
13014 if(r.data[prop] == value){
13024 getName: function()
13026 // returns hidden if it's set..
13027 if (!this.rendered) {return ''};
13028 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13032 onViewMove : function(e, t){
13033 this.inKeyMode = false;
13037 onViewOver : function(e, t){
13038 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13041 var item = this.view.findItemFromChild(t);
13044 var index = this.view.indexOf(item);
13045 this.select(index, false);
13050 onViewClick : function(view, doFocus, el, e)
13052 var index = this.view.getSelectedIndexes()[0];
13054 var r = this.store.getAt(index);
13058 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13065 Roo.each(this.tickItems, function(v,k){
13067 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13069 _this.tickItems.splice(k, 1);
13071 if(typeof(e) == 'undefined' && view == false){
13072 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13084 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13085 this.tickItems.push(r.data);
13088 if(typeof(e) == 'undefined' && view == false){
13089 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13096 this.onSelect(r, index);
13098 if(doFocus !== false && !this.blockFocus){
13099 this.inputEl().focus();
13104 restrictHeight : function(){
13105 //this.innerList.dom.style.height = '';
13106 //var inner = this.innerList.dom;
13107 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13108 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13109 //this.list.beginUpdate();
13110 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13111 this.list.alignTo(this.inputEl(), this.listAlign);
13112 this.list.alignTo(this.inputEl(), this.listAlign);
13113 //this.list.endUpdate();
13117 onEmptyResults : function(){
13119 if(this.tickable && this.editable){
13120 this.restrictHeight();
13128 * Returns true if the dropdown list is expanded, else false.
13130 isExpanded : function(){
13131 return this.list.isVisible();
13135 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13136 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13137 * @param {String} value The data value of the item to select
13138 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13139 * selected item if it is not currently in view (defaults to true)
13140 * @return {Boolean} True if the value matched an item in the list, else false
13142 selectByValue : function(v, scrollIntoView){
13143 if(v !== undefined && v !== null){
13144 var r = this.findRecord(this.valueField || this.displayField, v);
13146 this.select(this.store.indexOf(r), scrollIntoView);
13154 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13155 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13156 * @param {Number} index The zero-based index of the list item to select
13157 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13158 * selected item if it is not currently in view (defaults to true)
13160 select : function(index, scrollIntoView){
13161 this.selectedIndex = index;
13162 this.view.select(index);
13163 if(scrollIntoView !== false){
13164 var el = this.view.getNode(index);
13166 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13169 this.list.scrollChildIntoView(el, false);
13175 selectNext : function(){
13176 var ct = this.store.getCount();
13178 if(this.selectedIndex == -1){
13180 }else if(this.selectedIndex < ct-1){
13181 this.select(this.selectedIndex+1);
13187 selectPrev : function(){
13188 var ct = this.store.getCount();
13190 if(this.selectedIndex == -1){
13192 }else if(this.selectedIndex != 0){
13193 this.select(this.selectedIndex-1);
13199 onKeyUp : function(e){
13200 if(this.editable !== false && !e.isSpecialKey()){
13201 this.lastKey = e.getKey();
13202 this.dqTask.delay(this.queryDelay);
13207 validateBlur : function(){
13208 return !this.list || !this.list.isVisible();
13212 initQuery : function(){
13214 var v = this.getRawValue();
13216 if(this.tickable && this.editable){
13217 v = this.tickableInputEl().getValue();
13224 doForce : function(){
13225 if(this.inputEl().dom.value.length > 0){
13226 this.inputEl().dom.value =
13227 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13233 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13234 * query allowing the query action to be canceled if needed.
13235 * @param {String} query The SQL query to execute
13236 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13237 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13238 * saved in the current store (defaults to false)
13240 doQuery : function(q, forceAll){
13242 if(q === undefined || q === null){
13247 forceAll: forceAll,
13251 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13256 forceAll = qe.forceAll;
13257 if(forceAll === true || (q.length >= this.minChars)){
13259 this.hasQuery = true;
13261 if(this.lastQuery != q || this.alwaysQuery){
13262 this.lastQuery = q;
13263 if(this.mode == 'local'){
13264 this.selectedIndex = -1;
13266 this.store.clearFilter();
13269 if(this.specialFilter){
13270 this.fireEvent('specialfilter', this);
13275 this.store.filter(this.displayField, q);
13278 this.store.fireEvent("datachanged", this.store);
13285 this.store.baseParams[this.queryParam] = q;
13287 var options = {params : this.getParams(q)};
13290 options.add = true;
13291 options.params.start = this.page * this.pageSize;
13294 this.store.load(options);
13297 * this code will make the page width larger, at the beginning, the list not align correctly,
13298 * we should expand the list on onLoad
13299 * so command out it
13304 this.selectedIndex = -1;
13309 this.loadNext = false;
13313 getParams : function(q){
13315 //p[this.queryParam] = q;
13319 p.limit = this.pageSize;
13325 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13327 collapse : function(){
13328 if(!this.isExpanded()){
13335 this.hasFocus = false;
13337 this.cancelBtn.hide();
13338 this.trigger.show();
13341 this.tickableInputEl().dom.value = '';
13342 this.tickableInputEl().blur();
13347 Roo.get(document).un('mousedown', this.collapseIf, this);
13348 Roo.get(document).un('mousewheel', this.collapseIf, this);
13349 if (!this.editable) {
13350 Roo.get(document).un('keydown', this.listKeyPress, this);
13352 this.fireEvent('collapse', this);
13356 collapseIf : function(e){
13357 var in_combo = e.within(this.el);
13358 var in_list = e.within(this.list);
13359 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13361 if (in_combo || in_list || is_list) {
13362 //e.stopPropagation();
13367 this.onTickableFooterButtonClick(e, false, false);
13375 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13377 expand : function(){
13379 if(this.isExpanded() || !this.hasFocus){
13383 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13384 this.list.setWidth(lw);
13391 this.restrictHeight();
13395 this.tickItems = Roo.apply([], this.item);
13398 this.cancelBtn.show();
13399 this.trigger.hide();
13402 this.tickableInputEl().focus();
13407 Roo.get(document).on('mousedown', this.collapseIf, this);
13408 Roo.get(document).on('mousewheel', this.collapseIf, this);
13409 if (!this.editable) {
13410 Roo.get(document).on('keydown', this.listKeyPress, this);
13413 this.fireEvent('expand', this);
13417 // Implements the default empty TriggerField.onTriggerClick function
13418 onTriggerClick : function(e)
13420 Roo.log('trigger click');
13422 if(this.disabled || !this.triggerList){
13427 this.loadNext = false;
13429 if(this.isExpanded()){
13431 if (!this.blockFocus) {
13432 this.inputEl().focus();
13436 this.hasFocus = true;
13437 if(this.triggerAction == 'all') {
13438 this.doQuery(this.allQuery, true);
13440 this.doQuery(this.getRawValue());
13442 if (!this.blockFocus) {
13443 this.inputEl().focus();
13448 onTickableTriggerClick : function(e)
13455 this.loadNext = false;
13456 this.hasFocus = true;
13458 if(this.triggerAction == 'all') {
13459 this.doQuery(this.allQuery, true);
13461 this.doQuery(this.getRawValue());
13465 onSearchFieldClick : function(e)
13467 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13468 this.onTickableFooterButtonClick(e, false, false);
13472 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13477 this.loadNext = false;
13478 this.hasFocus = true;
13480 if(this.triggerAction == 'all') {
13481 this.doQuery(this.allQuery, true);
13483 this.doQuery(this.getRawValue());
13487 listKeyPress : function(e)
13489 //Roo.log('listkeypress');
13490 // scroll to first matching element based on key pres..
13491 if (e.isSpecialKey()) {
13494 var k = String.fromCharCode(e.getKey()).toUpperCase();
13497 var csel = this.view.getSelectedNodes();
13498 var cselitem = false;
13500 var ix = this.view.indexOf(csel[0]);
13501 cselitem = this.store.getAt(ix);
13502 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13508 this.store.each(function(v) {
13510 // start at existing selection.
13511 if (cselitem.id == v.id) {
13517 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13518 match = this.store.indexOf(v);
13524 if (match === false) {
13525 return true; // no more action?
13528 this.view.select(match);
13529 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13530 sn.scrollIntoView(sn.dom.parentNode, false);
13533 onViewScroll : function(e, t){
13535 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){
13539 this.hasQuery = true;
13541 this.loading = this.list.select('.loading', true).first();
13543 if(this.loading === null){
13544 this.list.createChild({
13546 cls: 'loading roo-select2-more-results roo-select2-active',
13547 html: 'Loading more results...'
13550 this.loading = this.list.select('.loading', true).first();
13552 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13554 this.loading.hide();
13557 this.loading.show();
13562 this.loadNext = true;
13564 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13569 addItem : function(o)
13571 var dv = ''; // display value
13573 if (this.displayField) {
13574 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13576 // this is an error condition!!!
13577 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13584 var choice = this.choices.createChild({
13586 cls: 'roo-select2-search-choice',
13595 cls: 'roo-select2-search-choice-close',
13600 }, this.searchField);
13602 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13604 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13612 this.inputEl().dom.value = '';
13617 onRemoveItem : function(e, _self, o)
13619 e.preventDefault();
13621 this.lastItem = Roo.apply([], this.item);
13623 var index = this.item.indexOf(o.data) * 1;
13626 Roo.log('not this item?!');
13630 this.item.splice(index, 1);
13635 this.fireEvent('remove', this, e);
13641 syncValue : function()
13643 if(!this.item.length){
13650 Roo.each(this.item, function(i){
13651 if(_this.valueField){
13652 value.push(i[_this.valueField]);
13659 this.value = value.join(',');
13661 if(this.hiddenField){
13662 this.hiddenField.dom.value = this.value;
13665 this.store.fireEvent("datachanged", this.store);
13668 clearItem : function()
13670 if(!this.multiple){
13676 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13684 if(this.tickable && !Roo.isTouch){
13685 this.view.refresh();
13689 inputEl: function ()
13691 if(Roo.isTouch && this.mobileTouchView){
13692 return this.el.select('input.form-control',true).first();
13696 return this.searchField;
13699 return this.el.select('input.form-control',true).first();
13703 onTickableFooterButtonClick : function(e, btn, el)
13705 e.preventDefault();
13707 this.lastItem = Roo.apply([], this.item);
13709 if(btn && btn.name == 'cancel'){
13710 this.tickItems = Roo.apply([], this.item);
13719 Roo.each(this.tickItems, function(o){
13727 validate : function()
13729 var v = this.getRawValue();
13732 v = this.getValue();
13735 if(this.disabled || this.allowBlank || v.length){
13740 this.markInvalid();
13744 tickableInputEl : function()
13746 if(!this.tickable || !this.editable){
13747 return this.inputEl();
13750 return this.inputEl().select('.roo-select2-search-field-input', true).first();
13754 getAutoCreateTouchView : function()
13759 cls: 'form-group' //input-group
13765 type : this.inputType,
13766 cls : 'form-control x-combo-noedit',
13767 autocomplete: 'new-password',
13768 placeholder : this.placeholder || '',
13773 input.name = this.name;
13777 input.cls += ' input-' + this.size;
13780 if (this.disabled) {
13781 input.disabled = true;
13792 inputblock.cls += ' input-group';
13794 inputblock.cn.unshift({
13796 cls : 'input-group-addon',
13801 if(this.removable && !this.multiple){
13802 inputblock.cls += ' roo-removable';
13804 inputblock.cn.push({
13807 cls : 'roo-combo-removable-btn close'
13811 if(this.hasFeedback && !this.allowBlank){
13813 inputblock.cls += ' has-feedback';
13815 inputblock.cn.push({
13817 cls: 'glyphicon form-control-feedback'
13824 inputblock.cls += (this.before) ? '' : ' input-group';
13826 inputblock.cn.push({
13828 cls : 'input-group-addon',
13839 cls: 'form-hidden-field'
13853 cls: 'form-hidden-field'
13857 cls: 'roo-select2-choices',
13861 cls: 'roo-select2-search-field',
13874 cls: 'roo-select2-container input-group',
13880 // if(!this.multiple && this.showToggleBtn){
13887 // if (this.caret != false) {
13890 // cls: 'fa fa-' + this.caret
13895 // combobox.cn.push({
13897 // cls : 'input-group-addon btn dropdown-toggle',
13902 // cls: 'combobox-clear',
13906 // cls: 'icon-remove'
13916 combobox.cls += ' roo-select2-container-multi';
13919 var align = this.labelAlign || this.parentLabelAlign();
13923 if(this.fieldLabel.length && this.labelWidth){
13925 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13926 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13931 cls : 'control-label ' + lw,
13932 html : this.fieldLabel
13944 var settings = this;
13946 ['xs','sm','md','lg'].map(function(size){
13947 if (settings[size]) {
13948 cfg.cls += ' col-' + size + '-' + settings[size];
13955 initTouchView : function()
13957 this.renderTouchView();
13959 this.touchViewEl.on('scroll', function(){
13960 this.el.dom.scrollTop = 0;
13963 this.originalValue = this.getValue();
13965 this.inputEl().on("touch", this.showTouchView, this);
13967 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13968 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13970 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13972 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13973 this.store.on('load', this.onTouchViewLoad, this);
13974 this.store.on('loadexception', this.onTouchViewLoadException, this);
13976 if(this.hiddenName){
13978 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13980 this.hiddenField.dom.value =
13981 this.hiddenValue !== undefined ? this.hiddenValue :
13982 this.value !== undefined ? this.value : '';
13984 this.el.dom.removeAttribute('name');
13985 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13989 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13990 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13993 if(this.removable && !this.multiple){
13994 var close = this.closeTriggerEl();
13996 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13997 close.on('click', this.removeBtnClick, this, close);
14001 * fix the bug in Safari iOS8
14003 this.inputEl().on("focus", function(e){
14004 document.activeElement.blur();
14012 renderTouchView : function()
14014 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14015 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14017 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14018 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14020 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14021 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14022 this.touchViewBodyEl.setStyle('overflow', 'auto');
14024 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14025 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14027 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14028 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14032 showTouchView : function()
14038 this.touchViewHeaderEl.hide();
14040 if(this.fieldLabel.length){
14041 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
14042 this.touchViewHeaderEl.show();
14045 this.touchViewEl.show();
14047 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14048 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14049 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14051 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14053 if(this.fieldLabel.length){
14054 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14057 this.touchViewBodyEl.setHeight(bodyHeight);
14061 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14063 this.touchViewEl.addClass('in');
14066 this.doTouchViewQuery();
14070 hideTouchView : function()
14072 this.touchViewEl.removeClass('in');
14076 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14078 this.touchViewEl.setStyle('display', 'none');
14083 setTouchViewValue : function()
14090 Roo.each(this.tickItems, function(o){
14095 this.hideTouchView();
14098 doTouchViewQuery : function()
14107 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14111 if(!this.alwaysQuery || this.mode == 'local'){
14112 this.onTouchViewLoad();
14119 onTouchViewBeforeLoad : function(combo,opts)
14125 onTouchViewLoad : function()
14127 if(this.store.getCount() < 1){
14128 this.onTouchViewEmptyResults();
14132 this.clearTouchView();
14134 var rawValue = this.getRawValue();
14136 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14138 this.tickItems = [];
14140 this.store.data.each(function(d, rowIndex){
14141 var row = this.touchViewListGroup.createChild(template);
14143 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14144 row.addClass(d.data.cls);
14147 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14150 html : d.data[this.displayField]
14153 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14154 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14157 row.removeClass('selected');
14158 if(!this.multiple && this.valueField &&
14159 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14162 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14163 row.addClass('selected');
14166 if(this.multiple && this.valueField &&
14167 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14171 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14172 this.tickItems.push(d.data);
14175 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14179 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14181 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14183 if(this.fieldLabel.length){
14184 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14187 var listHeight = this.touchViewListGroup.getHeight();
14191 if(firstChecked && listHeight > bodyHeight){
14192 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14197 onTouchViewLoadException : function()
14199 this.hideTouchView();
14202 onTouchViewEmptyResults : function()
14204 this.clearTouchView();
14206 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14208 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14212 clearTouchView : function()
14214 this.touchViewListGroup.dom.innerHTML = '';
14217 onTouchViewClick : function(e, el, o)
14219 e.preventDefault();
14222 var rowIndex = o.rowIndex;
14224 var r = this.store.getAt(rowIndex);
14226 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14228 if(!this.multiple){
14229 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14230 c.dom.removeAttribute('checked');
14233 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14235 this.setFromData(r.data);
14237 var close = this.closeTriggerEl();
14243 this.hideTouchView();
14245 this.fireEvent('select', this, r, rowIndex);
14250 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14251 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14252 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14256 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14257 this.addItem(r.data);
14258 this.tickItems.push(r.data);
14264 * @cfg {Boolean} grow
14268 * @cfg {Number} growMin
14272 * @cfg {Number} growMax
14281 Roo.apply(Roo.bootstrap.ComboBox, {
14285 cls: 'modal-header',
14307 cls: 'list-group-item',
14311 cls: 'roo-combobox-list-group-item-value'
14315 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14329 listItemCheckbox : {
14331 cls: 'list-group-item',
14335 cls: 'roo-combobox-list-group-item-value'
14339 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14355 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14360 cls: 'modal-footer',
14368 cls: 'col-xs-6 text-left',
14371 cls: 'btn btn-danger roo-touch-view-cancel',
14377 cls: 'col-xs-6 text-right',
14380 cls: 'btn btn-success roo-touch-view-ok',
14391 Roo.apply(Roo.bootstrap.ComboBox, {
14393 touchViewTemplate : {
14395 cls: 'modal fade roo-combobox-touch-view',
14399 cls: 'modal-dialog',
14400 style : 'position:fixed', // we have to fix position....
14404 cls: 'modal-content',
14406 Roo.bootstrap.ComboBox.header,
14407 Roo.bootstrap.ComboBox.body,
14408 Roo.bootstrap.ComboBox.footer
14417 * Ext JS Library 1.1.1
14418 * Copyright(c) 2006-2007, Ext JS, LLC.
14420 * Originally Released Under LGPL - original licence link has changed is not relivant.
14423 * <script type="text/javascript">
14428 * @extends Roo.util.Observable
14429 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14430 * This class also supports single and multi selection modes. <br>
14431 * Create a data model bound view:
14433 var store = new Roo.data.Store(...);
14435 var view = new Roo.View({
14437 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14439 singleSelect: true,
14440 selectedClass: "ydataview-selected",
14444 // listen for node click?
14445 view.on("click", function(vw, index, node, e){
14446 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14450 dataModel.load("foobar.xml");
14452 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14454 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14455 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14457 * Note: old style constructor is still suported (container, template, config)
14460 * Create a new View
14461 * @param {Object} config The config object
14464 Roo.View = function(config, depreciated_tpl, depreciated_config){
14466 this.parent = false;
14468 if (typeof(depreciated_tpl) == 'undefined') {
14469 // new way.. - universal constructor.
14470 Roo.apply(this, config);
14471 this.el = Roo.get(this.el);
14474 this.el = Roo.get(config);
14475 this.tpl = depreciated_tpl;
14476 Roo.apply(this, depreciated_config);
14478 this.wrapEl = this.el.wrap().wrap();
14479 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14482 if(typeof(this.tpl) == "string"){
14483 this.tpl = new Roo.Template(this.tpl);
14485 // support xtype ctors..
14486 this.tpl = new Roo.factory(this.tpl, Roo);
14490 this.tpl.compile();
14495 * @event beforeclick
14496 * Fires before a click is processed. Returns false to cancel the default action.
14497 * @param {Roo.View} this
14498 * @param {Number} index The index of the target node
14499 * @param {HTMLElement} node The target node
14500 * @param {Roo.EventObject} e The raw event object
14502 "beforeclick" : true,
14505 * Fires when a template node is clicked.
14506 * @param {Roo.View} this
14507 * @param {Number} index The index of the target node
14508 * @param {HTMLElement} node The target node
14509 * @param {Roo.EventObject} e The raw event object
14514 * Fires when a template node is double clicked.
14515 * @param {Roo.View} this
14516 * @param {Number} index The index of the target node
14517 * @param {HTMLElement} node The target node
14518 * @param {Roo.EventObject} e The raw event object
14522 * @event contextmenu
14523 * Fires when a template node is right clicked.
14524 * @param {Roo.View} this
14525 * @param {Number} index The index of the target node
14526 * @param {HTMLElement} node The target node
14527 * @param {Roo.EventObject} e The raw event object
14529 "contextmenu" : true,
14531 * @event selectionchange
14532 * Fires when the selected nodes change.
14533 * @param {Roo.View} this
14534 * @param {Array} selections Array of the selected nodes
14536 "selectionchange" : true,
14539 * @event beforeselect
14540 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14541 * @param {Roo.View} this
14542 * @param {HTMLElement} node The node to be selected
14543 * @param {Array} selections Array of currently selected nodes
14545 "beforeselect" : true,
14547 * @event preparedata
14548 * Fires on every row to render, to allow you to change the data.
14549 * @param {Roo.View} this
14550 * @param {Object} data to be rendered (change this)
14552 "preparedata" : true
14560 "click": this.onClick,
14561 "dblclick": this.onDblClick,
14562 "contextmenu": this.onContextMenu,
14566 this.selections = [];
14568 this.cmp = new Roo.CompositeElementLite([]);
14570 this.store = Roo.factory(this.store, Roo.data);
14571 this.setStore(this.store, true);
14574 if ( this.footer && this.footer.xtype) {
14576 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14578 this.footer.dataSource = this.store;
14579 this.footer.container = fctr;
14580 this.footer = Roo.factory(this.footer, Roo);
14581 fctr.insertFirst(this.el);
14583 // this is a bit insane - as the paging toolbar seems to detach the el..
14584 // dom.parentNode.parentNode.parentNode
14585 // they get detached?
14589 Roo.View.superclass.constructor.call(this);
14594 Roo.extend(Roo.View, Roo.util.Observable, {
14597 * @cfg {Roo.data.Store} store Data store to load data from.
14602 * @cfg {String|Roo.Element} el The container element.
14607 * @cfg {String|Roo.Template} tpl The template used by this View
14611 * @cfg {String} dataName the named area of the template to use as the data area
14612 * Works with domtemplates roo-name="name"
14616 * @cfg {String} selectedClass The css class to add to selected nodes
14618 selectedClass : "x-view-selected",
14620 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14625 * @cfg {String} text to display on mask (default Loading)
14629 * @cfg {Boolean} multiSelect Allow multiple selection
14631 multiSelect : false,
14633 * @cfg {Boolean} singleSelect Allow single selection
14635 singleSelect: false,
14638 * @cfg {Boolean} toggleSelect - selecting
14640 toggleSelect : false,
14643 * @cfg {Boolean} tickable - selecting
14648 * Returns the element this view is bound to.
14649 * @return {Roo.Element}
14651 getEl : function(){
14652 return this.wrapEl;
14658 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14660 refresh : function(){
14661 //Roo.log('refresh');
14664 // if we are using something like 'domtemplate', then
14665 // the what gets used is:
14666 // t.applySubtemplate(NAME, data, wrapping data..)
14667 // the outer template then get' applied with
14668 // the store 'extra data'
14669 // and the body get's added to the
14670 // roo-name="data" node?
14671 // <span class='roo-tpl-{name}'></span> ?????
14675 this.clearSelections();
14676 this.el.update("");
14678 var records = this.store.getRange();
14679 if(records.length < 1) {
14681 // is this valid?? = should it render a template??
14683 this.el.update(this.emptyText);
14687 if (this.dataName) {
14688 this.el.update(t.apply(this.store.meta)); //????
14689 el = this.el.child('.roo-tpl-' + this.dataName);
14692 for(var i = 0, len = records.length; i < len; i++){
14693 var data = this.prepareData(records[i].data, i, records[i]);
14694 this.fireEvent("preparedata", this, data, i, records[i]);
14696 var d = Roo.apply({}, data);
14699 Roo.apply(d, {'roo-id' : Roo.id()});
14703 Roo.each(this.parent.item, function(item){
14704 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14707 Roo.apply(d, {'roo-data-checked' : 'checked'});
14711 html[html.length] = Roo.util.Format.trim(
14713 t.applySubtemplate(this.dataName, d, this.store.meta) :
14720 el.update(html.join(""));
14721 this.nodes = el.dom.childNodes;
14722 this.updateIndexes(0);
14727 * Function to override to reformat the data that is sent to
14728 * the template for each node.
14729 * DEPRICATED - use the preparedata event handler.
14730 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14731 * a JSON object for an UpdateManager bound view).
14733 prepareData : function(data, index, record)
14735 this.fireEvent("preparedata", this, data, index, record);
14739 onUpdate : function(ds, record){
14740 // Roo.log('on update');
14741 this.clearSelections();
14742 var index = this.store.indexOf(record);
14743 var n = this.nodes[index];
14744 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14745 n.parentNode.removeChild(n);
14746 this.updateIndexes(index, index);
14752 onAdd : function(ds, records, index)
14754 //Roo.log(['on Add', ds, records, index] );
14755 this.clearSelections();
14756 if(this.nodes.length == 0){
14760 var n = this.nodes[index];
14761 for(var i = 0, len = records.length; i < len; i++){
14762 var d = this.prepareData(records[i].data, i, records[i]);
14764 this.tpl.insertBefore(n, d);
14767 this.tpl.append(this.el, d);
14770 this.updateIndexes(index);
14773 onRemove : function(ds, record, index){
14774 // Roo.log('onRemove');
14775 this.clearSelections();
14776 var el = this.dataName ?
14777 this.el.child('.roo-tpl-' + this.dataName) :
14780 el.dom.removeChild(this.nodes[index]);
14781 this.updateIndexes(index);
14785 * Refresh an individual node.
14786 * @param {Number} index
14788 refreshNode : function(index){
14789 this.onUpdate(this.store, this.store.getAt(index));
14792 updateIndexes : function(startIndex, endIndex){
14793 var ns = this.nodes;
14794 startIndex = startIndex || 0;
14795 endIndex = endIndex || ns.length - 1;
14796 for(var i = startIndex; i <= endIndex; i++){
14797 ns[i].nodeIndex = i;
14802 * Changes the data store this view uses and refresh the view.
14803 * @param {Store} store
14805 setStore : function(store, initial){
14806 if(!initial && this.store){
14807 this.store.un("datachanged", this.refresh);
14808 this.store.un("add", this.onAdd);
14809 this.store.un("remove", this.onRemove);
14810 this.store.un("update", this.onUpdate);
14811 this.store.un("clear", this.refresh);
14812 this.store.un("beforeload", this.onBeforeLoad);
14813 this.store.un("load", this.onLoad);
14814 this.store.un("loadexception", this.onLoad);
14818 store.on("datachanged", this.refresh, this);
14819 store.on("add", this.onAdd, this);
14820 store.on("remove", this.onRemove, this);
14821 store.on("update", this.onUpdate, this);
14822 store.on("clear", this.refresh, this);
14823 store.on("beforeload", this.onBeforeLoad, this);
14824 store.on("load", this.onLoad, this);
14825 store.on("loadexception", this.onLoad, this);
14833 * onbeforeLoad - masks the loading area.
14836 onBeforeLoad : function(store,opts)
14838 //Roo.log('onBeforeLoad');
14840 this.el.update("");
14842 this.el.mask(this.mask ? this.mask : "Loading" );
14844 onLoad : function ()
14851 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14852 * @param {HTMLElement} node
14853 * @return {HTMLElement} The template node
14855 findItemFromChild : function(node){
14856 var el = this.dataName ?
14857 this.el.child('.roo-tpl-' + this.dataName,true) :
14860 if(!node || node.parentNode == el){
14863 var p = node.parentNode;
14864 while(p && p != el){
14865 if(p.parentNode == el){
14874 onClick : function(e){
14875 var item = this.findItemFromChild(e.getTarget());
14877 var index = this.indexOf(item);
14878 if(this.onItemClick(item, index, e) !== false){
14879 this.fireEvent("click", this, index, item, e);
14882 this.clearSelections();
14887 onContextMenu : function(e){
14888 var item = this.findItemFromChild(e.getTarget());
14890 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14895 onDblClick : function(e){
14896 var item = this.findItemFromChild(e.getTarget());
14898 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14902 onItemClick : function(item, index, e)
14904 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14907 if (this.toggleSelect) {
14908 var m = this.isSelected(item) ? 'unselect' : 'select';
14911 _t[m](item, true, false);
14914 if(this.multiSelect || this.singleSelect){
14915 if(this.multiSelect && e.shiftKey && this.lastSelection){
14916 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14918 this.select(item, this.multiSelect && e.ctrlKey);
14919 this.lastSelection = item;
14922 if(!this.tickable){
14923 e.preventDefault();
14931 * Get the number of selected nodes.
14934 getSelectionCount : function(){
14935 return this.selections.length;
14939 * Get the currently selected nodes.
14940 * @return {Array} An array of HTMLElements
14942 getSelectedNodes : function(){
14943 return this.selections;
14947 * Get the indexes of the selected nodes.
14950 getSelectedIndexes : function(){
14951 var indexes = [], s = this.selections;
14952 for(var i = 0, len = s.length; i < len; i++){
14953 indexes.push(s[i].nodeIndex);
14959 * Clear all selections
14960 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14962 clearSelections : function(suppressEvent){
14963 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14964 this.cmp.elements = this.selections;
14965 this.cmp.removeClass(this.selectedClass);
14966 this.selections = [];
14967 if(!suppressEvent){
14968 this.fireEvent("selectionchange", this, this.selections);
14974 * Returns true if the passed node is selected
14975 * @param {HTMLElement/Number} node The node or node index
14976 * @return {Boolean}
14978 isSelected : function(node){
14979 var s = this.selections;
14983 node = this.getNode(node);
14984 return s.indexOf(node) !== -1;
14989 * @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
14990 * @param {Boolean} keepExisting (optional) true to keep existing selections
14991 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14993 select : function(nodeInfo, keepExisting, suppressEvent){
14994 if(nodeInfo instanceof Array){
14996 this.clearSelections(true);
14998 for(var i = 0, len = nodeInfo.length; i < len; i++){
14999 this.select(nodeInfo[i], true, true);
15003 var node = this.getNode(nodeInfo);
15004 if(!node || this.isSelected(node)){
15005 return; // already selected.
15008 this.clearSelections(true);
15011 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15012 Roo.fly(node).addClass(this.selectedClass);
15013 this.selections.push(node);
15014 if(!suppressEvent){
15015 this.fireEvent("selectionchange", this, this.selections);
15023 * @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
15024 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15025 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15027 unselect : function(nodeInfo, keepExisting, suppressEvent)
15029 if(nodeInfo instanceof Array){
15030 Roo.each(this.selections, function(s) {
15031 this.unselect(s, nodeInfo);
15035 var node = this.getNode(nodeInfo);
15036 if(!node || !this.isSelected(node)){
15037 //Roo.log("not selected");
15038 return; // not selected.
15042 Roo.each(this.selections, function(s) {
15044 Roo.fly(node).removeClass(this.selectedClass);
15051 this.selections= ns;
15052 this.fireEvent("selectionchange", this, this.selections);
15056 * Gets a template node.
15057 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15058 * @return {HTMLElement} The node or null if it wasn't found
15060 getNode : function(nodeInfo){
15061 if(typeof nodeInfo == "string"){
15062 return document.getElementById(nodeInfo);
15063 }else if(typeof nodeInfo == "number"){
15064 return this.nodes[nodeInfo];
15070 * Gets a range template nodes.
15071 * @param {Number} startIndex
15072 * @param {Number} endIndex
15073 * @return {Array} An array of nodes
15075 getNodes : function(start, end){
15076 var ns = this.nodes;
15077 start = start || 0;
15078 end = typeof end == "undefined" ? ns.length - 1 : end;
15081 for(var i = start; i <= end; i++){
15085 for(var i = start; i >= end; i--){
15093 * Finds the index of the passed node
15094 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15095 * @return {Number} The index of the node or -1
15097 indexOf : function(node){
15098 node = this.getNode(node);
15099 if(typeof node.nodeIndex == "number"){
15100 return node.nodeIndex;
15102 var ns = this.nodes;
15103 for(var i = 0, len = ns.length; i < len; i++){
15114 * based on jquery fullcalendar
15118 Roo.bootstrap = Roo.bootstrap || {};
15120 * @class Roo.bootstrap.Calendar
15121 * @extends Roo.bootstrap.Component
15122 * Bootstrap Calendar class
15123 * @cfg {Boolean} loadMask (true|false) default false
15124 * @cfg {Object} header generate the user specific header of the calendar, default false
15127 * Create a new Container
15128 * @param {Object} config The config object
15133 Roo.bootstrap.Calendar = function(config){
15134 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15138 * Fires when a date is selected
15139 * @param {DatePicker} this
15140 * @param {Date} date The selected date
15144 * @event monthchange
15145 * Fires when the displayed month changes
15146 * @param {DatePicker} this
15147 * @param {Date} date The selected month
15149 'monthchange': true,
15151 * @event evententer
15152 * Fires when mouse over an event
15153 * @param {Calendar} this
15154 * @param {event} Event
15156 'evententer': true,
15158 * @event eventleave
15159 * Fires when the mouse leaves an
15160 * @param {Calendar} this
15163 'eventleave': true,
15165 * @event eventclick
15166 * Fires when the mouse click an
15167 * @param {Calendar} this
15176 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15179 * @cfg {Number} startDay
15180 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15188 getAutoCreate : function(){
15191 var fc_button = function(name, corner, style, content ) {
15192 return Roo.apply({},{
15194 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15196 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15199 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15210 style : 'width:100%',
15217 cls : 'fc-header-left',
15219 fc_button('prev', 'left', 'arrow', '‹' ),
15220 fc_button('next', 'right', 'arrow', '›' ),
15221 { tag: 'span', cls: 'fc-header-space' },
15222 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15230 cls : 'fc-header-center',
15234 cls: 'fc-header-title',
15237 html : 'month / year'
15245 cls : 'fc-header-right',
15247 /* fc_button('month', 'left', '', 'month' ),
15248 fc_button('week', '', '', 'week' ),
15249 fc_button('day', 'right', '', 'day' )
15261 header = this.header;
15264 var cal_heads = function() {
15266 // fixme - handle this.
15268 for (var i =0; i < Date.dayNames.length; i++) {
15269 var d = Date.dayNames[i];
15272 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15273 html : d.substring(0,3)
15277 ret[0].cls += ' fc-first';
15278 ret[6].cls += ' fc-last';
15281 var cal_cell = function(n) {
15284 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15289 cls: 'fc-day-number',
15293 cls: 'fc-day-content',
15297 style: 'position: relative;' // height: 17px;
15309 var cal_rows = function() {
15312 for (var r = 0; r < 6; r++) {
15319 for (var i =0; i < Date.dayNames.length; i++) {
15320 var d = Date.dayNames[i];
15321 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15324 row.cn[0].cls+=' fc-first';
15325 row.cn[0].cn[0].style = 'min-height:90px';
15326 row.cn[6].cls+=' fc-last';
15330 ret[0].cls += ' fc-first';
15331 ret[4].cls += ' fc-prev-last';
15332 ret[5].cls += ' fc-last';
15339 cls: 'fc-border-separate',
15340 style : 'width:100%',
15348 cls : 'fc-first fc-last',
15366 cls : 'fc-content',
15367 style : "position: relative;",
15370 cls : 'fc-view fc-view-month fc-grid',
15371 style : 'position: relative',
15372 unselectable : 'on',
15375 cls : 'fc-event-container',
15376 style : 'position:absolute;z-index:8;top:0;left:0;'
15394 initEvents : function()
15397 throw "can not find store for calendar";
15403 style: "text-align:center",
15407 style: "background-color:white;width:50%;margin:250 auto",
15411 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15422 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15424 var size = this.el.select('.fc-content', true).first().getSize();
15425 this.maskEl.setSize(size.width, size.height);
15426 this.maskEl.enableDisplayMode("block");
15427 if(!this.loadMask){
15428 this.maskEl.hide();
15431 this.store = Roo.factory(this.store, Roo.data);
15432 this.store.on('load', this.onLoad, this);
15433 this.store.on('beforeload', this.onBeforeLoad, this);
15437 this.cells = this.el.select('.fc-day',true);
15438 //Roo.log(this.cells);
15439 this.textNodes = this.el.query('.fc-day-number');
15440 this.cells.addClassOnOver('fc-state-hover');
15442 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15443 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15444 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15445 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15447 this.on('monthchange', this.onMonthChange, this);
15449 this.update(new Date().clearTime());
15452 resize : function() {
15453 var sz = this.el.getSize();
15455 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15456 this.el.select('.fc-day-content div',true).setHeight(34);
15461 showPrevMonth : function(e){
15462 this.update(this.activeDate.add("mo", -1));
15464 showToday : function(e){
15465 this.update(new Date().clearTime());
15468 showNextMonth : function(e){
15469 this.update(this.activeDate.add("mo", 1));
15473 showPrevYear : function(){
15474 this.update(this.activeDate.add("y", -1));
15478 showNextYear : function(){
15479 this.update(this.activeDate.add("y", 1));
15484 update : function(date)
15486 var vd = this.activeDate;
15487 this.activeDate = date;
15488 // if(vd && this.el){
15489 // var t = date.getTime();
15490 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15491 // Roo.log('using add remove');
15493 // this.fireEvent('monthchange', this, date);
15495 // this.cells.removeClass("fc-state-highlight");
15496 // this.cells.each(function(c){
15497 // if(c.dateValue == t){
15498 // c.addClass("fc-state-highlight");
15499 // setTimeout(function(){
15500 // try{c.dom.firstChild.focus();}catch(e){}
15510 var days = date.getDaysInMonth();
15512 var firstOfMonth = date.getFirstDateOfMonth();
15513 var startingPos = firstOfMonth.getDay()-this.startDay;
15515 if(startingPos < this.startDay){
15519 var pm = date.add(Date.MONTH, -1);
15520 var prevStart = pm.getDaysInMonth()-startingPos;
15522 this.cells = this.el.select('.fc-day',true);
15523 this.textNodes = this.el.query('.fc-day-number');
15524 this.cells.addClassOnOver('fc-state-hover');
15526 var cells = this.cells.elements;
15527 var textEls = this.textNodes;
15529 Roo.each(cells, function(cell){
15530 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15533 days += startingPos;
15535 // convert everything to numbers so it's fast
15536 var day = 86400000;
15537 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15540 //Roo.log(prevStart);
15542 var today = new Date().clearTime().getTime();
15543 var sel = date.clearTime().getTime();
15544 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15545 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15546 var ddMatch = this.disabledDatesRE;
15547 var ddText = this.disabledDatesText;
15548 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15549 var ddaysText = this.disabledDaysText;
15550 var format = this.format;
15552 var setCellClass = function(cal, cell){
15556 //Roo.log('set Cell Class');
15558 var t = d.getTime();
15562 cell.dateValue = t;
15564 cell.className += " fc-today";
15565 cell.className += " fc-state-highlight";
15566 cell.title = cal.todayText;
15569 // disable highlight in other month..
15570 //cell.className += " fc-state-highlight";
15575 cell.className = " fc-state-disabled";
15576 cell.title = cal.minText;
15580 cell.className = " fc-state-disabled";
15581 cell.title = cal.maxText;
15585 if(ddays.indexOf(d.getDay()) != -1){
15586 cell.title = ddaysText;
15587 cell.className = " fc-state-disabled";
15590 if(ddMatch && format){
15591 var fvalue = d.dateFormat(format);
15592 if(ddMatch.test(fvalue)){
15593 cell.title = ddText.replace("%0", fvalue);
15594 cell.className = " fc-state-disabled";
15598 if (!cell.initialClassName) {
15599 cell.initialClassName = cell.dom.className;
15602 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15607 for(; i < startingPos; i++) {
15608 textEls[i].innerHTML = (++prevStart);
15609 d.setDate(d.getDate()+1);
15611 cells[i].className = "fc-past fc-other-month";
15612 setCellClass(this, cells[i]);
15617 for(; i < days; i++){
15618 intDay = i - startingPos + 1;
15619 textEls[i].innerHTML = (intDay);
15620 d.setDate(d.getDate()+1);
15622 cells[i].className = ''; // "x-date-active";
15623 setCellClass(this, cells[i]);
15627 for(; i < 42; i++) {
15628 textEls[i].innerHTML = (++extraDays);
15629 d.setDate(d.getDate()+1);
15631 cells[i].className = "fc-future fc-other-month";
15632 setCellClass(this, cells[i]);
15635 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15637 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15639 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15640 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15642 if(totalRows != 6){
15643 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15644 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15647 this.fireEvent('monthchange', this, date);
15651 if(!this.internalRender){
15652 var main = this.el.dom.firstChild;
15653 var w = main.offsetWidth;
15654 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15655 Roo.fly(main).setWidth(w);
15656 this.internalRender = true;
15657 // opera does not respect the auto grow header center column
15658 // then, after it gets a width opera refuses to recalculate
15659 // without a second pass
15660 if(Roo.isOpera && !this.secondPass){
15661 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15662 this.secondPass = true;
15663 this.update.defer(10, this, [date]);
15670 findCell : function(dt) {
15671 dt = dt.clearTime().getTime();
15673 this.cells.each(function(c){
15674 //Roo.log("check " +c.dateValue + '?=' + dt);
15675 if(c.dateValue == dt){
15685 findCells : function(ev) {
15686 var s = ev.start.clone().clearTime().getTime();
15688 var e= ev.end.clone().clearTime().getTime();
15691 this.cells.each(function(c){
15692 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15694 if(c.dateValue > e){
15697 if(c.dateValue < s){
15706 // findBestRow: function(cells)
15710 // for (var i =0 ; i < cells.length;i++) {
15711 // ret = Math.max(cells[i].rows || 0,ret);
15718 addItem : function(ev)
15720 // look for vertical location slot in
15721 var cells = this.findCells(ev);
15723 // ev.row = this.findBestRow(cells);
15725 // work out the location.
15729 for(var i =0; i < cells.length; i++) {
15731 cells[i].row = cells[0].row;
15734 cells[i].row = cells[i].row + 1;
15744 if (crow.start.getY() == cells[i].getY()) {
15746 crow.end = cells[i];
15763 cells[0].events.push(ev);
15765 this.calevents.push(ev);
15768 clearEvents: function() {
15770 if(!this.calevents){
15774 Roo.each(this.cells.elements, function(c){
15780 Roo.each(this.calevents, function(e) {
15781 Roo.each(e.els, function(el) {
15782 el.un('mouseenter' ,this.onEventEnter, this);
15783 el.un('mouseleave' ,this.onEventLeave, this);
15788 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15794 renderEvents: function()
15798 this.cells.each(function(c) {
15807 if(c.row != c.events.length){
15808 r = 4 - (4 - (c.row - c.events.length));
15811 c.events = ev.slice(0, r);
15812 c.more = ev.slice(r);
15814 if(c.more.length && c.more.length == 1){
15815 c.events.push(c.more.pop());
15818 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15822 this.cells.each(function(c) {
15824 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15827 for (var e = 0; e < c.events.length; e++){
15828 var ev = c.events[e];
15829 var rows = ev.rows;
15831 for(var i = 0; i < rows.length; i++) {
15833 // how many rows should it span..
15836 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15837 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15839 unselectable : "on",
15842 cls: 'fc-event-inner',
15846 // cls: 'fc-event-time',
15847 // html : cells.length > 1 ? '' : ev.time
15851 cls: 'fc-event-title',
15852 html : String.format('{0}', ev.title)
15859 cls: 'ui-resizable-handle ui-resizable-e',
15860 html : '  '
15867 cfg.cls += ' fc-event-start';
15869 if ((i+1) == rows.length) {
15870 cfg.cls += ' fc-event-end';
15873 var ctr = _this.el.select('.fc-event-container',true).first();
15874 var cg = ctr.createChild(cfg);
15876 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15877 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15879 var r = (c.more.length) ? 1 : 0;
15880 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15881 cg.setWidth(ebox.right - sbox.x -2);
15883 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15884 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15885 cg.on('click', _this.onEventClick, _this, ev);
15896 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15897 style : 'position: absolute',
15898 unselectable : "on",
15901 cls: 'fc-event-inner',
15905 cls: 'fc-event-title',
15913 cls: 'ui-resizable-handle ui-resizable-e',
15914 html : '  '
15920 var ctr = _this.el.select('.fc-event-container',true).first();
15921 var cg = ctr.createChild(cfg);
15923 var sbox = c.select('.fc-day-content',true).first().getBox();
15924 var ebox = c.select('.fc-day-content',true).first().getBox();
15926 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15927 cg.setWidth(ebox.right - sbox.x -2);
15929 cg.on('click', _this.onMoreEventClick, _this, c.more);
15939 onEventEnter: function (e, el,event,d) {
15940 this.fireEvent('evententer', this, el, event);
15943 onEventLeave: function (e, el,event,d) {
15944 this.fireEvent('eventleave', this, el, event);
15947 onEventClick: function (e, el,event,d) {
15948 this.fireEvent('eventclick', this, el, event);
15951 onMonthChange: function () {
15955 onMoreEventClick: function(e, el, more)
15959 this.calpopover.placement = 'right';
15960 this.calpopover.setTitle('More');
15962 this.calpopover.setContent('');
15964 var ctr = this.calpopover.el.select('.popover-content', true).first();
15966 Roo.each(more, function(m){
15968 cls : 'fc-event-hori fc-event-draggable',
15971 var cg = ctr.createChild(cfg);
15973 cg.on('click', _this.onEventClick, _this, m);
15976 this.calpopover.show(el);
15981 onLoad: function ()
15983 this.calevents = [];
15986 if(this.store.getCount() > 0){
15987 this.store.data.each(function(d){
15990 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15991 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15992 time : d.data.start_time,
15993 title : d.data.title,
15994 description : d.data.description,
15995 venue : d.data.venue
16000 this.renderEvents();
16002 if(this.calevents.length && this.loadMask){
16003 this.maskEl.hide();
16007 onBeforeLoad: function()
16009 this.clearEvents();
16011 this.maskEl.show();
16025 * @class Roo.bootstrap.Popover
16026 * @extends Roo.bootstrap.Component
16027 * Bootstrap Popover class
16028 * @cfg {String} html contents of the popover (or false to use children..)
16029 * @cfg {String} title of popover (or false to hide)
16030 * @cfg {String} placement how it is placed
16031 * @cfg {String} trigger click || hover (or false to trigger manually)
16032 * @cfg {String} over what (parent or false to trigger manually.)
16033 * @cfg {Number} delay - delay before showing
16036 * Create a new Popover
16037 * @param {Object} config The config object
16040 Roo.bootstrap.Popover = function(config){
16041 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16047 * After the popover show
16049 * @param {Roo.bootstrap.Popover} this
16054 * After the popover hide
16056 * @param {Roo.bootstrap.Popover} this
16062 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16064 title: 'Fill in a title',
16067 placement : 'right',
16068 trigger : 'hover', // hover
16074 can_build_overlaid : false,
16076 getChildContainer : function()
16078 return this.el.select('.popover-content',true).first();
16081 getAutoCreate : function(){
16084 cls : 'popover roo-dynamic',
16085 style: 'display:block',
16091 cls : 'popover-inner',
16095 cls: 'popover-title',
16099 cls : 'popover-content',
16110 setTitle: function(str)
16113 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16115 setContent: function(str)
16118 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16120 // as it get's added to the bottom of the page.
16121 onRender : function(ct, position)
16123 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16125 var cfg = Roo.apply({}, this.getAutoCreate());
16129 cfg.cls += ' ' + this.cls;
16132 cfg.style = this.style;
16134 //Roo.log("adding to ");
16135 this.el = Roo.get(document.body).createChild(cfg, position);
16136 // Roo.log(this.el);
16141 initEvents : function()
16143 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16144 this.el.enableDisplayMode('block');
16146 if (this.over === false) {
16149 if (this.triggers === false) {
16152 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16153 var triggers = this.trigger ? this.trigger.split(' ') : [];
16154 Roo.each(triggers, function(trigger) {
16156 if (trigger == 'click') {
16157 on_el.on('click', this.toggle, this);
16158 } else if (trigger != 'manual') {
16159 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16160 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16162 on_el.on(eventIn ,this.enter, this);
16163 on_el.on(eventOut, this.leave, this);
16174 toggle : function () {
16175 this.hoverState == 'in' ? this.leave() : this.enter();
16178 enter : function () {
16180 clearTimeout(this.timeout);
16182 this.hoverState = 'in';
16184 if (!this.delay || !this.delay.show) {
16189 this.timeout = setTimeout(function () {
16190 if (_t.hoverState == 'in') {
16193 }, this.delay.show)
16196 leave : function() {
16197 clearTimeout(this.timeout);
16199 this.hoverState = 'out';
16201 if (!this.delay || !this.delay.hide) {
16206 this.timeout = setTimeout(function () {
16207 if (_t.hoverState == 'out') {
16210 }, this.delay.hide)
16213 show : function (on_el)
16216 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16220 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16221 if (this.html !== false) {
16222 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16224 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16225 if (!this.title.length) {
16226 this.el.select('.popover-title',true).hide();
16229 var placement = typeof this.placement == 'function' ?
16230 this.placement.call(this, this.el, on_el) :
16233 var autoToken = /\s?auto?\s?/i;
16234 var autoPlace = autoToken.test(placement);
16236 placement = placement.replace(autoToken, '') || 'top';
16240 //this.el.setXY([0,0]);
16242 this.el.dom.style.display='block';
16243 this.el.addClass(placement);
16245 //this.el.appendTo(on_el);
16247 var p = this.getPosition();
16248 var box = this.el.getBox();
16253 var align = Roo.bootstrap.Popover.alignment[placement];
16254 this.el.alignTo(on_el, align[0],align[1]);
16255 //var arrow = this.el.select('.arrow',true).first();
16256 //arrow.set(align[2],
16258 this.el.addClass('in');
16261 if (this.el.hasClass('fade')) {
16265 this.hoverState = 'in';
16267 this.fireEvent('show', this);
16272 this.el.setXY([0,0]);
16273 this.el.removeClass('in');
16275 this.hoverState = null;
16277 this.fireEvent('hide', this);
16282 Roo.bootstrap.Popover.alignment = {
16283 'left' : ['r-l', [-10,0], 'right'],
16284 'right' : ['l-r', [10,0], 'left'],
16285 'bottom' : ['t-b', [0,10], 'top'],
16286 'top' : [ 'b-t', [0,-10], 'bottom']
16297 * @class Roo.bootstrap.Progress
16298 * @extends Roo.bootstrap.Component
16299 * Bootstrap Progress class
16300 * @cfg {Boolean} striped striped of the progress bar
16301 * @cfg {Boolean} active animated of the progress bar
16305 * Create a new Progress
16306 * @param {Object} config The config object
16309 Roo.bootstrap.Progress = function(config){
16310 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16313 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16318 getAutoCreate : function(){
16326 cfg.cls += ' progress-striped';
16330 cfg.cls += ' active';
16349 * @class Roo.bootstrap.ProgressBar
16350 * @extends Roo.bootstrap.Component
16351 * Bootstrap ProgressBar class
16352 * @cfg {Number} aria_valuenow aria-value now
16353 * @cfg {Number} aria_valuemin aria-value min
16354 * @cfg {Number} aria_valuemax aria-value max
16355 * @cfg {String} label label for the progress bar
16356 * @cfg {String} panel (success | info | warning | danger )
16357 * @cfg {String} role role of the progress bar
16358 * @cfg {String} sr_only text
16362 * Create a new ProgressBar
16363 * @param {Object} config The config object
16366 Roo.bootstrap.ProgressBar = function(config){
16367 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16370 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16374 aria_valuemax : 100,
16380 getAutoCreate : function()
16385 cls: 'progress-bar',
16386 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16398 cfg.role = this.role;
16401 if(this.aria_valuenow){
16402 cfg['aria-valuenow'] = this.aria_valuenow;
16405 if(this.aria_valuemin){
16406 cfg['aria-valuemin'] = this.aria_valuemin;
16409 if(this.aria_valuemax){
16410 cfg['aria-valuemax'] = this.aria_valuemax;
16413 if(this.label && !this.sr_only){
16414 cfg.html = this.label;
16418 cfg.cls += ' progress-bar-' + this.panel;
16424 update : function(aria_valuenow)
16426 this.aria_valuenow = aria_valuenow;
16428 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16443 * @class Roo.bootstrap.TabGroup
16444 * @extends Roo.bootstrap.Column
16445 * Bootstrap Column class
16446 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16447 * @cfg {Boolean} carousel true to make the group behave like a carousel
16448 * @cfg {Boolean} bullets show bullets for the panels
16449 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16450 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16451 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16454 * Create a new TabGroup
16455 * @param {Object} config The config object
16458 Roo.bootstrap.TabGroup = function(config){
16459 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16461 this.navId = Roo.id();
16464 Roo.bootstrap.TabGroup.register(this);
16468 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16471 transition : false,
16476 slideOnTouch : false,
16478 getAutoCreate : function()
16480 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16482 cfg.cls += ' tab-content';
16484 if (this.carousel) {
16485 cfg.cls += ' carousel slide';
16488 cls : 'carousel-inner'
16491 if(this.bullets && !Roo.isTouch){
16494 cls : 'carousel-bullets',
16498 if(this.bullets_cls){
16499 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16502 for (var i = 0; i < this.bullets; i++){
16504 cls : 'bullet bullet-' + i
16512 cfg.cn[0].cn = bullets;
16519 initEvents: function()
16521 if(Roo.isTouch && this.slideOnTouch){
16522 this.el.on("touchstart", this.onTouchStart, this);
16525 if(this.autoslide){
16528 this.slideFn = window.setInterval(function() {
16529 _this.showPanelNext();
16535 onTouchStart : function(e, el, o)
16537 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16541 this.showPanelNext();
16544 getChildContainer : function()
16546 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16550 * register a Navigation item
16551 * @param {Roo.bootstrap.NavItem} the navitem to add
16553 register : function(item)
16555 this.tabs.push( item);
16556 item.navId = this.navId; // not really needed..
16561 getActivePanel : function()
16564 Roo.each(this.tabs, function(t) {
16574 getPanelByName : function(n)
16577 Roo.each(this.tabs, function(t) {
16578 if (t.tabId == n) {
16586 indexOfPanel : function(p)
16589 Roo.each(this.tabs, function(t,i) {
16590 if (t.tabId == p.tabId) {
16599 * show a specific panel
16600 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16601 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16603 showPanel : function (pan)
16605 if(this.transition || typeof(pan) == 'undefined'){
16606 Roo.log("waiting for the transitionend");
16610 if (typeof(pan) == 'number') {
16611 pan = this.tabs[pan];
16614 if (typeof(pan) == 'string') {
16615 pan = this.getPanelByName(pan);
16618 var cur = this.getActivePanel();
16621 Roo.log('pan or acitve pan is undefined');
16625 if (pan.tabId == this.getActivePanel().tabId) {
16629 if (false === cur.fireEvent('beforedeactivate')) {
16633 if(this.bullets > 0 && !Roo.isTouch){
16634 this.setActiveBullet(this.indexOfPanel(pan));
16637 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16639 this.transition = true;
16640 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16641 var lr = dir == 'next' ? 'left' : 'right';
16642 pan.el.addClass(dir); // or prev
16643 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16644 cur.el.addClass(lr); // or right
16645 pan.el.addClass(lr);
16648 cur.el.on('transitionend', function() {
16649 Roo.log("trans end?");
16651 pan.el.removeClass([lr,dir]);
16652 pan.setActive(true);
16654 cur.el.removeClass([lr]);
16655 cur.setActive(false);
16657 _this.transition = false;
16659 }, this, { single: true } );
16664 cur.setActive(false);
16665 pan.setActive(true);
16670 showPanelNext : function()
16672 var i = this.indexOfPanel(this.getActivePanel());
16674 if (i >= this.tabs.length - 1 && !this.autoslide) {
16678 if (i >= this.tabs.length - 1 && this.autoslide) {
16682 this.showPanel(this.tabs[i+1]);
16685 showPanelPrev : function()
16687 var i = this.indexOfPanel(this.getActivePanel());
16689 if (i < 1 && !this.autoslide) {
16693 if (i < 1 && this.autoslide) {
16694 i = this.tabs.length;
16697 this.showPanel(this.tabs[i-1]);
16701 addBullet: function()
16703 if(!this.bullets || Roo.isTouch){
16706 var ctr = this.el.select('.carousel-bullets',true).first();
16707 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16708 var bullet = ctr.createChild({
16709 cls : 'bullet bullet-' + i
16710 },ctr.dom.lastChild);
16715 bullet.on('click', (function(e, el, o, ii, t){
16717 e.preventDefault();
16719 this.showPanel(ii);
16721 if(this.autoslide && this.slideFn){
16722 clearInterval(this.slideFn);
16723 this.slideFn = window.setInterval(function() {
16724 _this.showPanelNext();
16728 }).createDelegate(this, [i, bullet], true));
16733 setActiveBullet : function(i)
16739 Roo.each(this.el.select('.bullet', true).elements, function(el){
16740 el.removeClass('selected');
16743 var bullet = this.el.select('.bullet-' + i, true).first();
16749 bullet.addClass('selected');
16760 Roo.apply(Roo.bootstrap.TabGroup, {
16764 * register a Navigation Group
16765 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16767 register : function(navgrp)
16769 this.groups[navgrp.navId] = navgrp;
16773 * fetch a Navigation Group based on the navigation ID
16774 * if one does not exist , it will get created.
16775 * @param {string} the navgroup to add
16776 * @returns {Roo.bootstrap.NavGroup} the navgroup
16778 get: function(navId) {
16779 if (typeof(this.groups[navId]) == 'undefined') {
16780 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16782 return this.groups[navId] ;
16797 * @class Roo.bootstrap.TabPanel
16798 * @extends Roo.bootstrap.Component
16799 * Bootstrap TabPanel class
16800 * @cfg {Boolean} active panel active
16801 * @cfg {String} html panel content
16802 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16803 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16807 * Create a new TabPanel
16808 * @param {Object} config The config object
16811 Roo.bootstrap.TabPanel = function(config){
16812 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16816 * Fires when the active status changes
16817 * @param {Roo.bootstrap.TabPanel} this
16818 * @param {Boolean} state the new state
16823 * @event beforedeactivate
16824 * Fires before a tab is de-activated - can be used to do validation on a form.
16825 * @param {Roo.bootstrap.TabPanel} this
16826 * @return {Boolean} false if there is an error
16829 'beforedeactivate': true
16832 this.tabId = this.tabId || Roo.id();
16836 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16843 getAutoCreate : function(){
16846 // item is needed for carousel - not sure if it has any effect otherwise
16847 cls: 'tab-pane item',
16848 html: this.html || ''
16852 cfg.cls += ' active';
16856 cfg.tabId = this.tabId;
16863 initEvents: function()
16865 var p = this.parent();
16866 this.navId = this.navId || p.navId;
16868 if (typeof(this.navId) != 'undefined') {
16869 // not really needed.. but just in case.. parent should be a NavGroup.
16870 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16874 var i = tg.tabs.length - 1;
16876 if(this.active && tg.bullets > 0 && i < tg.bullets){
16877 tg.setActiveBullet(i);
16884 onRender : function(ct, position)
16886 // Roo.log("Call onRender: " + this.xtype);
16888 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16896 setActive: function(state)
16898 Roo.log("panel - set active " + this.tabId + "=" + state);
16900 this.active = state;
16902 this.el.removeClass('active');
16904 } else if (!this.el.hasClass('active')) {
16905 this.el.addClass('active');
16908 this.fireEvent('changed', this, state);
16925 * @class Roo.bootstrap.DateField
16926 * @extends Roo.bootstrap.Input
16927 * Bootstrap DateField class
16928 * @cfg {Number} weekStart default 0
16929 * @cfg {String} viewMode default empty, (months|years)
16930 * @cfg {String} minViewMode default empty, (months|years)
16931 * @cfg {Number} startDate default -Infinity
16932 * @cfg {Number} endDate default Infinity
16933 * @cfg {Boolean} todayHighlight default false
16934 * @cfg {Boolean} todayBtn default false
16935 * @cfg {Boolean} calendarWeeks default false
16936 * @cfg {Object} daysOfWeekDisabled default empty
16937 * @cfg {Boolean} singleMode default false (true | false)
16939 * @cfg {Boolean} keyboardNavigation default true
16940 * @cfg {String} language default en
16943 * Create a new DateField
16944 * @param {Object} config The config object
16947 Roo.bootstrap.DateField = function(config){
16948 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16952 * Fires when this field show.
16953 * @param {Roo.bootstrap.DateField} this
16954 * @param {Mixed} date The date value
16959 * Fires when this field hide.
16960 * @param {Roo.bootstrap.DateField} this
16961 * @param {Mixed} date The date value
16966 * Fires when select a date.
16967 * @param {Roo.bootstrap.DateField} this
16968 * @param {Mixed} date The date value
16972 * @event beforeselect
16973 * Fires when before select a date.
16974 * @param {Roo.bootstrap.DateField} this
16975 * @param {Mixed} date The date value
16977 beforeselect : true
16981 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16984 * @cfg {String} format
16985 * The default date format string which can be overriden for localization support. The format must be
16986 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16990 * @cfg {String} altFormats
16991 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16992 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16994 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17002 todayHighlight : false,
17008 keyboardNavigation: true,
17010 calendarWeeks: false,
17012 startDate: -Infinity,
17016 daysOfWeekDisabled: [],
17020 singleMode : false,
17022 UTCDate: function()
17024 return new Date(Date.UTC.apply(Date, arguments));
17027 UTCToday: function()
17029 var today = new Date();
17030 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17033 getDate: function() {
17034 var d = this.getUTCDate();
17035 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17038 getUTCDate: function() {
17042 setDate: function(d) {
17043 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17046 setUTCDate: function(d) {
17048 this.setValue(this.formatDate(this.date));
17051 onRender: function(ct, position)
17054 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17056 this.language = this.language || 'en';
17057 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17058 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17060 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17061 this.format = this.format || 'm/d/y';
17062 this.isInline = false;
17063 this.isInput = true;
17064 this.component = this.el.select('.add-on', true).first() || false;
17065 this.component = (this.component && this.component.length === 0) ? false : this.component;
17066 this.hasInput = this.component && this.inputEL().length;
17068 if (typeof(this.minViewMode === 'string')) {
17069 switch (this.minViewMode) {
17071 this.minViewMode = 1;
17074 this.minViewMode = 2;
17077 this.minViewMode = 0;
17082 if (typeof(this.viewMode === 'string')) {
17083 switch (this.viewMode) {
17096 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17098 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17100 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17102 this.picker().on('mousedown', this.onMousedown, this);
17103 this.picker().on('click', this.onClick, this);
17105 this.picker().addClass('datepicker-dropdown');
17107 this.startViewMode = this.viewMode;
17109 if(this.singleMode){
17110 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17111 v.setVisibilityMode(Roo.Element.DISPLAY);
17115 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17116 v.setStyle('width', '189px');
17120 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17121 if(!this.calendarWeeks){
17126 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17127 v.attr('colspan', function(i, val){
17128 return parseInt(val) + 1;
17133 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17135 this.setStartDate(this.startDate);
17136 this.setEndDate(this.endDate);
17138 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17145 if(this.isInline) {
17150 picker : function()
17152 return this.pickerEl;
17153 // return this.el.select('.datepicker', true).first();
17156 fillDow: function()
17158 var dowCnt = this.weekStart;
17167 if(this.calendarWeeks){
17175 while (dowCnt < this.weekStart + 7) {
17179 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17183 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17186 fillMonths: function()
17189 var months = this.picker().select('>.datepicker-months td', true).first();
17191 months.dom.innerHTML = '';
17197 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17200 months.createChild(month);
17207 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;
17209 if (this.date < this.startDate) {
17210 this.viewDate = new Date(this.startDate);
17211 } else if (this.date > this.endDate) {
17212 this.viewDate = new Date(this.endDate);
17214 this.viewDate = new Date(this.date);
17222 var d = new Date(this.viewDate),
17223 year = d.getUTCFullYear(),
17224 month = d.getUTCMonth(),
17225 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17226 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17227 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17228 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17229 currentDate = this.date && this.date.valueOf(),
17230 today = this.UTCToday();
17232 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17234 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17236 // this.picker.select('>tfoot th.today').
17237 // .text(dates[this.language].today)
17238 // .toggle(this.todayBtn !== false);
17240 this.updateNavArrows();
17243 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17245 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17247 prevMonth.setUTCDate(day);
17249 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17251 var nextMonth = new Date(prevMonth);
17253 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17255 nextMonth = nextMonth.valueOf();
17257 var fillMonths = false;
17259 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17261 while(prevMonth.valueOf() < nextMonth) {
17264 if (prevMonth.getUTCDay() === this.weekStart) {
17266 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17274 if(this.calendarWeeks){
17275 // ISO 8601: First week contains first thursday.
17276 // ISO also states week starts on Monday, but we can be more abstract here.
17278 // Start of current week: based on weekstart/current date
17279 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17280 // Thursday of this week
17281 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17282 // First Thursday of year, year from thursday
17283 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17284 // Calendar week: ms between thursdays, div ms per day, div 7 days
17285 calWeek = (th - yth) / 864e5 / 7 + 1;
17287 fillMonths.cn.push({
17295 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17297 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17300 if (this.todayHighlight &&
17301 prevMonth.getUTCFullYear() == today.getFullYear() &&
17302 prevMonth.getUTCMonth() == today.getMonth() &&
17303 prevMonth.getUTCDate() == today.getDate()) {
17304 clsName += ' today';
17307 if (currentDate && prevMonth.valueOf() === currentDate) {
17308 clsName += ' active';
17311 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17312 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17313 clsName += ' disabled';
17316 fillMonths.cn.push({
17318 cls: 'day ' + clsName,
17319 html: prevMonth.getDate()
17322 prevMonth.setDate(prevMonth.getDate()+1);
17325 var currentYear = this.date && this.date.getUTCFullYear();
17326 var currentMonth = this.date && this.date.getUTCMonth();
17328 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17330 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17331 v.removeClass('active');
17333 if(currentYear === year && k === currentMonth){
17334 v.addClass('active');
17337 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17338 v.addClass('disabled');
17344 year = parseInt(year/10, 10) * 10;
17346 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17348 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17351 for (var i = -1; i < 11; i++) {
17352 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17354 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17362 showMode: function(dir)
17365 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17368 Roo.each(this.picker().select('>div',true).elements, function(v){
17369 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17372 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17377 if(this.isInline) {
17381 this.picker().removeClass(['bottom', 'top']);
17383 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17385 * place to the top of element!
17389 this.picker().addClass('top');
17390 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17395 this.picker().addClass('bottom');
17397 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17400 parseDate : function(value)
17402 if(!value || value instanceof Date){
17405 var v = Date.parseDate(value, this.format);
17406 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17407 v = Date.parseDate(value, 'Y-m-d');
17409 if(!v && this.altFormats){
17410 if(!this.altFormatsArray){
17411 this.altFormatsArray = this.altFormats.split("|");
17413 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17414 v = Date.parseDate(value, this.altFormatsArray[i]);
17420 formatDate : function(date, fmt)
17422 return (!date || !(date instanceof Date)) ?
17423 date : date.dateFormat(fmt || this.format);
17426 onFocus : function()
17428 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17432 onBlur : function()
17434 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17436 var d = this.inputEl().getValue();
17445 this.picker().show();
17449 this.fireEvent('show', this, this.date);
17454 if(this.isInline) {
17457 this.picker().hide();
17458 this.viewMode = this.startViewMode;
17461 this.fireEvent('hide', this, this.date);
17465 onMousedown: function(e)
17467 e.stopPropagation();
17468 e.preventDefault();
17473 Roo.bootstrap.DateField.superclass.keyup.call(this);
17477 setValue: function(v)
17479 if(this.fireEvent('beforeselect', this, v) !== false){
17480 var d = new Date(this.parseDate(v) ).clearTime();
17482 if(isNaN(d.getTime())){
17483 this.date = this.viewDate = '';
17484 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17488 v = this.formatDate(d);
17490 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17492 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17496 this.fireEvent('select', this, this.date);
17500 getValue: function()
17502 return this.formatDate(this.date);
17505 fireKey: function(e)
17507 if (!this.picker().isVisible()){
17508 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17514 var dateChanged = false,
17516 newDate, newViewDate;
17521 e.preventDefault();
17525 if (!this.keyboardNavigation) {
17528 dir = e.keyCode == 37 ? -1 : 1;
17531 newDate = this.moveYear(this.date, dir);
17532 newViewDate = this.moveYear(this.viewDate, dir);
17533 } else if (e.shiftKey){
17534 newDate = this.moveMonth(this.date, dir);
17535 newViewDate = this.moveMonth(this.viewDate, dir);
17537 newDate = new Date(this.date);
17538 newDate.setUTCDate(this.date.getUTCDate() + dir);
17539 newViewDate = new Date(this.viewDate);
17540 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17542 if (this.dateWithinRange(newDate)){
17543 this.date = newDate;
17544 this.viewDate = newViewDate;
17545 this.setValue(this.formatDate(this.date));
17547 e.preventDefault();
17548 dateChanged = true;
17553 if (!this.keyboardNavigation) {
17556 dir = e.keyCode == 38 ? -1 : 1;
17558 newDate = this.moveYear(this.date, dir);
17559 newViewDate = this.moveYear(this.viewDate, dir);
17560 } else if (e.shiftKey){
17561 newDate = this.moveMonth(this.date, dir);
17562 newViewDate = this.moveMonth(this.viewDate, dir);
17564 newDate = new Date(this.date);
17565 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17566 newViewDate = new Date(this.viewDate);
17567 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17569 if (this.dateWithinRange(newDate)){
17570 this.date = newDate;
17571 this.viewDate = newViewDate;
17572 this.setValue(this.formatDate(this.date));
17574 e.preventDefault();
17575 dateChanged = true;
17579 this.setValue(this.formatDate(this.date));
17581 e.preventDefault();
17584 this.setValue(this.formatDate(this.date));
17598 onClick: function(e)
17600 e.stopPropagation();
17601 e.preventDefault();
17603 var target = e.getTarget();
17605 if(target.nodeName.toLowerCase() === 'i'){
17606 target = Roo.get(target).dom.parentNode;
17609 var nodeName = target.nodeName;
17610 var className = target.className;
17611 var html = target.innerHTML;
17612 //Roo.log(nodeName);
17614 switch(nodeName.toLowerCase()) {
17616 switch(className) {
17622 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17623 switch(this.viewMode){
17625 this.viewDate = this.moveMonth(this.viewDate, dir);
17629 this.viewDate = this.moveYear(this.viewDate, dir);
17635 var date = new Date();
17636 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17638 this.setValue(this.formatDate(this.date));
17645 if (className.indexOf('disabled') < 0) {
17646 this.viewDate.setUTCDate(1);
17647 if (className.indexOf('month') > -1) {
17648 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17650 var year = parseInt(html, 10) || 0;
17651 this.viewDate.setUTCFullYear(year);
17655 if(this.singleMode){
17656 this.setValue(this.formatDate(this.viewDate));
17667 //Roo.log(className);
17668 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17669 var day = parseInt(html, 10) || 1;
17670 var year = this.viewDate.getUTCFullYear(),
17671 month = this.viewDate.getUTCMonth();
17673 if (className.indexOf('old') > -1) {
17680 } else if (className.indexOf('new') > -1) {
17688 //Roo.log([year,month,day]);
17689 this.date = this.UTCDate(year, month, day,0,0,0,0);
17690 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17692 //Roo.log(this.formatDate(this.date));
17693 this.setValue(this.formatDate(this.date));
17700 setStartDate: function(startDate)
17702 this.startDate = startDate || -Infinity;
17703 if (this.startDate !== -Infinity) {
17704 this.startDate = this.parseDate(this.startDate);
17707 this.updateNavArrows();
17710 setEndDate: function(endDate)
17712 this.endDate = endDate || Infinity;
17713 if (this.endDate !== Infinity) {
17714 this.endDate = this.parseDate(this.endDate);
17717 this.updateNavArrows();
17720 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17722 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17723 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17724 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17726 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17727 return parseInt(d, 10);
17730 this.updateNavArrows();
17733 updateNavArrows: function()
17735 if(this.singleMode){
17739 var d = new Date(this.viewDate),
17740 year = d.getUTCFullYear(),
17741 month = d.getUTCMonth();
17743 Roo.each(this.picker().select('.prev', true).elements, function(v){
17745 switch (this.viewMode) {
17748 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17754 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17761 Roo.each(this.picker().select('.next', true).elements, function(v){
17763 switch (this.viewMode) {
17766 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17772 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17780 moveMonth: function(date, dir)
17785 var new_date = new Date(date.valueOf()),
17786 day = new_date.getUTCDate(),
17787 month = new_date.getUTCMonth(),
17788 mag = Math.abs(dir),
17790 dir = dir > 0 ? 1 : -1;
17793 // If going back one month, make sure month is not current month
17794 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17796 return new_date.getUTCMonth() == month;
17798 // If going forward one month, make sure month is as expected
17799 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17801 return new_date.getUTCMonth() != new_month;
17803 new_month = month + dir;
17804 new_date.setUTCMonth(new_month);
17805 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17806 if (new_month < 0 || new_month > 11) {
17807 new_month = (new_month + 12) % 12;
17810 // For magnitudes >1, move one month at a time...
17811 for (var i=0; i<mag; i++) {
17812 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17813 new_date = this.moveMonth(new_date, dir);
17815 // ...then reset the day, keeping it in the new month
17816 new_month = new_date.getUTCMonth();
17817 new_date.setUTCDate(day);
17819 return new_month != new_date.getUTCMonth();
17822 // Common date-resetting loop -- if date is beyond end of month, make it
17825 new_date.setUTCDate(--day);
17826 new_date.setUTCMonth(new_month);
17831 moveYear: function(date, dir)
17833 return this.moveMonth(date, dir*12);
17836 dateWithinRange: function(date)
17838 return date >= this.startDate && date <= this.endDate;
17844 this.picker().remove();
17849 Roo.apply(Roo.bootstrap.DateField, {
17860 html: '<i class="fa fa-arrow-left"/>'
17870 html: '<i class="fa fa-arrow-right"/>'
17912 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17913 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17914 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17915 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17916 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17929 navFnc: 'FullYear',
17934 navFnc: 'FullYear',
17939 Roo.apply(Roo.bootstrap.DateField, {
17943 cls: 'datepicker dropdown-menu roo-dynamic',
17947 cls: 'datepicker-days',
17951 cls: 'table-condensed',
17953 Roo.bootstrap.DateField.head,
17957 Roo.bootstrap.DateField.footer
17964 cls: 'datepicker-months',
17968 cls: 'table-condensed',
17970 Roo.bootstrap.DateField.head,
17971 Roo.bootstrap.DateField.content,
17972 Roo.bootstrap.DateField.footer
17979 cls: 'datepicker-years',
17983 cls: 'table-condensed',
17985 Roo.bootstrap.DateField.head,
17986 Roo.bootstrap.DateField.content,
17987 Roo.bootstrap.DateField.footer
18006 * @class Roo.bootstrap.TimeField
18007 * @extends Roo.bootstrap.Input
18008 * Bootstrap DateField class
18012 * Create a new TimeField
18013 * @param {Object} config The config object
18016 Roo.bootstrap.TimeField = function(config){
18017 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18021 * Fires when this field show.
18022 * @param {Roo.bootstrap.DateField} thisthis
18023 * @param {Mixed} date The date value
18028 * Fires when this field hide.
18029 * @param {Roo.bootstrap.DateField} this
18030 * @param {Mixed} date The date value
18035 * Fires when select a date.
18036 * @param {Roo.bootstrap.DateField} this
18037 * @param {Mixed} date The date value
18043 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18046 * @cfg {String} format
18047 * The default time format string which can be overriden for localization support. The format must be
18048 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18052 onRender: function(ct, position)
18055 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18057 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18059 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18061 this.pop = this.picker().select('>.datepicker-time',true).first();
18062 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18064 this.picker().on('mousedown', this.onMousedown, this);
18065 this.picker().on('click', this.onClick, this);
18067 this.picker().addClass('datepicker-dropdown');
18072 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18073 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18074 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18075 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18076 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18077 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18081 fireKey: function(e){
18082 if (!this.picker().isVisible()){
18083 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18089 e.preventDefault();
18097 this.onTogglePeriod();
18100 this.onIncrementMinutes();
18103 this.onDecrementMinutes();
18112 onClick: function(e) {
18113 e.stopPropagation();
18114 e.preventDefault();
18117 picker : function()
18119 return this.el.select('.datepicker', true).first();
18122 fillTime: function()
18124 var time = this.pop.select('tbody', true).first();
18126 time.dom.innerHTML = '';
18141 cls: 'hours-up glyphicon glyphicon-chevron-up'
18161 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18182 cls: 'timepicker-hour',
18197 cls: 'timepicker-minute',
18212 cls: 'btn btn-primary period',
18234 cls: 'hours-down glyphicon glyphicon-chevron-down'
18254 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18272 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18279 var hours = this.time.getHours();
18280 var minutes = this.time.getMinutes();
18293 hours = hours - 12;
18297 hours = '0' + hours;
18301 minutes = '0' + minutes;
18304 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18305 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18306 this.pop.select('button', true).first().dom.innerHTML = period;
18312 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18314 var cls = ['bottom'];
18316 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18323 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18328 this.picker().addClass(cls.join('-'));
18332 Roo.each(cls, function(c){
18334 _this.picker().setTop(_this.inputEl().getHeight());
18338 _this.picker().setTop(0 - _this.picker().getHeight());
18343 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18347 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18354 onFocus : function()
18356 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18360 onBlur : function()
18362 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18368 this.picker().show();
18373 this.fireEvent('show', this, this.date);
18378 this.picker().hide();
18381 this.fireEvent('hide', this, this.date);
18384 setTime : function()
18387 this.setValue(this.time.format(this.format));
18389 this.fireEvent('select', this, this.date);
18394 onMousedown: function(e){
18395 e.stopPropagation();
18396 e.preventDefault();
18399 onIncrementHours: function()
18401 Roo.log('onIncrementHours');
18402 this.time = this.time.add(Date.HOUR, 1);
18407 onDecrementHours: function()
18409 Roo.log('onDecrementHours');
18410 this.time = this.time.add(Date.HOUR, -1);
18414 onIncrementMinutes: function()
18416 Roo.log('onIncrementMinutes');
18417 this.time = this.time.add(Date.MINUTE, 1);
18421 onDecrementMinutes: function()
18423 Roo.log('onDecrementMinutes');
18424 this.time = this.time.add(Date.MINUTE, -1);
18428 onTogglePeriod: function()
18430 Roo.log('onTogglePeriod');
18431 this.time = this.time.add(Date.HOUR, 12);
18438 Roo.apply(Roo.bootstrap.TimeField, {
18468 cls: 'btn btn-info ok',
18480 Roo.apply(Roo.bootstrap.TimeField, {
18484 cls: 'datepicker dropdown-menu',
18488 cls: 'datepicker-time',
18492 cls: 'table-condensed',
18494 Roo.bootstrap.TimeField.content,
18495 Roo.bootstrap.TimeField.footer
18514 * @class Roo.bootstrap.MonthField
18515 * @extends Roo.bootstrap.Input
18516 * Bootstrap MonthField class
18518 * @cfg {String} language default en
18521 * Create a new MonthField
18522 * @param {Object} config The config object
18525 Roo.bootstrap.MonthField = function(config){
18526 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18531 * Fires when this field show.
18532 * @param {Roo.bootstrap.MonthField} this
18533 * @param {Mixed} date The date value
18538 * Fires when this field hide.
18539 * @param {Roo.bootstrap.MonthField} this
18540 * @param {Mixed} date The date value
18545 * Fires when select a date.
18546 * @param {Roo.bootstrap.MonthField} this
18547 * @param {String} oldvalue The old value
18548 * @param {String} newvalue The new value
18554 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18556 onRender: function(ct, position)
18559 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18561 this.language = this.language || 'en';
18562 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18563 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18565 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18566 this.isInline = false;
18567 this.isInput = true;
18568 this.component = this.el.select('.add-on', true).first() || false;
18569 this.component = (this.component && this.component.length === 0) ? false : this.component;
18570 this.hasInput = this.component && this.inputEL().length;
18572 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18574 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18576 this.picker().on('mousedown', this.onMousedown, this);
18577 this.picker().on('click', this.onClick, this);
18579 this.picker().addClass('datepicker-dropdown');
18581 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18582 v.setStyle('width', '189px');
18589 if(this.isInline) {
18595 setValue: function(v, suppressEvent)
18597 var o = this.getValue();
18599 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18603 if(suppressEvent !== true){
18604 this.fireEvent('select', this, o, v);
18609 getValue: function()
18614 onClick: function(e)
18616 e.stopPropagation();
18617 e.preventDefault();
18619 var target = e.getTarget();
18621 if(target.nodeName.toLowerCase() === 'i'){
18622 target = Roo.get(target).dom.parentNode;
18625 var nodeName = target.nodeName;
18626 var className = target.className;
18627 var html = target.innerHTML;
18629 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18633 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18635 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18641 picker : function()
18643 return this.pickerEl;
18646 fillMonths: function()
18649 var months = this.picker().select('>.datepicker-months td', true).first();
18651 months.dom.innerHTML = '';
18657 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18660 months.createChild(month);
18669 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18670 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18673 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18674 e.removeClass('active');
18676 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18677 e.addClass('active');
18684 if(this.isInline) {
18688 this.picker().removeClass(['bottom', 'top']);
18690 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18692 * place to the top of element!
18696 this.picker().addClass('top');
18697 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18702 this.picker().addClass('bottom');
18704 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18707 onFocus : function()
18709 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18713 onBlur : function()
18715 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18717 var d = this.inputEl().getValue();
18726 this.picker().show();
18727 this.picker().select('>.datepicker-months', true).first().show();
18731 this.fireEvent('show', this, this.date);
18736 if(this.isInline) {
18739 this.picker().hide();
18740 this.fireEvent('hide', this, this.date);
18744 onMousedown: function(e)
18746 e.stopPropagation();
18747 e.preventDefault();
18752 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18756 fireKey: function(e)
18758 if (!this.picker().isVisible()){
18759 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18770 e.preventDefault();
18774 dir = e.keyCode == 37 ? -1 : 1;
18776 this.vIndex = this.vIndex + dir;
18778 if(this.vIndex < 0){
18782 if(this.vIndex > 11){
18786 if(isNaN(this.vIndex)){
18790 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18796 dir = e.keyCode == 38 ? -1 : 1;
18798 this.vIndex = this.vIndex + dir * 4;
18800 if(this.vIndex < 0){
18804 if(this.vIndex > 11){
18808 if(isNaN(this.vIndex)){
18812 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18817 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18818 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18822 e.preventDefault();
18825 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18826 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18842 this.picker().remove();
18847 Roo.apply(Roo.bootstrap.MonthField, {
18866 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18867 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18872 Roo.apply(Roo.bootstrap.MonthField, {
18876 cls: 'datepicker dropdown-menu roo-dynamic',
18880 cls: 'datepicker-months',
18884 cls: 'table-condensed',
18886 Roo.bootstrap.DateField.content
18906 * @class Roo.bootstrap.CheckBox
18907 * @extends Roo.bootstrap.Input
18908 * Bootstrap CheckBox class
18910 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18911 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18912 * @cfg {String} boxLabel The text that appears beside the checkbox
18913 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18914 * @cfg {Boolean} checked initnal the element
18915 * @cfg {Boolean} inline inline the element (default false)
18916 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18919 * Create a new CheckBox
18920 * @param {Object} config The config object
18923 Roo.bootstrap.CheckBox = function(config){
18924 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18929 * Fires when the element is checked or unchecked.
18930 * @param {Roo.bootstrap.CheckBox} this This input
18931 * @param {Boolean} checked The new checked value
18938 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18940 inputType: 'checkbox',
18948 getAutoCreate : function()
18950 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18956 cfg.cls = 'form-group ' + this.inputType; //input-group
18959 cfg.cls += ' ' + this.inputType + '-inline';
18965 type : this.inputType,
18966 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18967 cls : 'roo-' + this.inputType, //'form-box',
18968 placeholder : this.placeholder || ''
18972 if (this.weight) { // Validity check?
18973 cfg.cls += " " + this.inputType + "-" + this.weight;
18976 if (this.disabled) {
18977 input.disabled=true;
18981 input.checked = this.checked;
18985 input.name = this.name;
18989 input.cls += ' input-' + this.size;
18994 ['xs','sm','md','lg'].map(function(size){
18995 if (settings[size]) {
18996 cfg.cls += ' col-' + size + '-' + settings[size];
19000 var inputblock = input;
19002 if (this.before || this.after) {
19005 cls : 'input-group',
19010 inputblock.cn.push({
19012 cls : 'input-group-addon',
19017 inputblock.cn.push(input);
19020 inputblock.cn.push({
19022 cls : 'input-group-addon',
19029 if (align ==='left' && this.fieldLabel.length) {
19030 // Roo.log("left and has label");
19036 cls : 'control-label col-md-' + this.labelWidth,
19037 html : this.fieldLabel
19041 cls : "col-md-" + (12 - this.labelWidth),
19048 } else if ( this.fieldLabel.length) {
19049 // Roo.log(" label");
19053 tag: this.boxLabel ? 'span' : 'label',
19055 cls: 'control-label box-input-label',
19056 //cls : 'input-group-addon',
19057 html : this.fieldLabel
19067 // Roo.log(" no label && no align");
19068 cfg.cn = [ inputblock ] ;
19074 var boxLabelCfg = {
19076 //'for': id, // box label is handled by onclick - so no for...
19078 html: this.boxLabel
19082 boxLabelCfg.tooltip = this.tooltip;
19085 cfg.cn.push(boxLabelCfg);
19095 * return the real input element.
19097 inputEl: function ()
19099 return this.el.select('input.roo-' + this.inputType,true).first();
19102 labelEl: function()
19104 return this.el.select('label.control-label',true).first();
19106 /* depricated... */
19110 return this.labelEl();
19113 boxLabelEl: function()
19115 return this.el.select('label.box-label',true).first();
19118 initEvents : function()
19120 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19122 this.inputEl().on('click', this.onClick, this);
19124 if (this.boxLabel) {
19125 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19128 this.startValue = this.getValue();
19131 Roo.bootstrap.CheckBox.register(this);
19135 onClick : function()
19137 this.setChecked(!this.checked);
19140 setChecked : function(state,suppressEvent)
19142 this.startValue = this.getValue();
19144 if(this.inputType == 'radio'){
19146 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19147 e.dom.checked = false;
19150 this.inputEl().dom.checked = true;
19152 this.inputEl().dom.value = this.inputValue;
19154 if(suppressEvent !== true){
19155 this.fireEvent('check', this, true);
19163 this.checked = state;
19165 this.inputEl().dom.checked = state;
19167 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19169 if(suppressEvent !== true){
19170 this.fireEvent('check', this, state);
19176 getValue : function()
19178 if(this.inputType == 'radio'){
19179 return this.getGroupValue();
19182 return this.inputEl().getValue();
19186 getGroupValue : function()
19188 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19192 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19195 setValue : function(v,suppressEvent)
19197 if(this.inputType == 'radio'){
19198 this.setGroupValue(v, suppressEvent);
19202 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19207 setGroupValue : function(v, suppressEvent)
19209 this.startValue = this.getValue();
19211 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19212 e.dom.checked = false;
19214 if(e.dom.value == v){
19215 e.dom.checked = true;
19219 if(suppressEvent !== true){
19220 this.fireEvent('check', this, true);
19228 validate : function()
19232 (this.inputType == 'radio' && this.validateRadio()) ||
19233 (this.inputType == 'checkbox' && this.validateCheckbox())
19239 this.markInvalid();
19243 validateRadio : function()
19247 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19248 if(!e.dom.checked){
19260 validateCheckbox : function()
19263 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19266 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19274 for(var i in group){
19279 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19286 * Mark this field as valid
19288 markValid : function()
19290 if(this.allowBlank){
19296 this.fireEvent('valid', this);
19298 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19301 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19308 if(this.inputType == 'radio'){
19309 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19310 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19311 e.findParent('.form-group', false, true).addClass(_this.validClass);
19318 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19319 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19323 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19329 for(var i in group){
19330 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19331 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19336 * Mark this field as invalid
19337 * @param {String} msg The validation message
19339 markInvalid : function(msg)
19341 if(this.allowBlank){
19347 this.fireEvent('invalid', this, msg);
19349 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19352 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19356 label.markInvalid();
19359 if(this.inputType == 'radio'){
19360 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19361 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19362 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19369 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19370 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19374 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19380 for(var i in group){
19381 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19382 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19389 Roo.apply(Roo.bootstrap.CheckBox, {
19394 * register a CheckBox Group
19395 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19397 register : function(checkbox)
19399 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19400 this.groups[checkbox.groupId] = {};
19403 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19407 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19411 * fetch a CheckBox Group based on the group ID
19412 * @param {string} the group ID
19413 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19415 get: function(groupId) {
19416 if (typeof(this.groups[groupId]) == 'undefined') {
19420 return this.groups[groupId] ;
19432 *<div class="radio">
19434 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19435 Option one is this and that—be sure to include why it's great
19442 *<label class="radio-inline">fieldLabel</label>
19443 *<label class="radio-inline">
19444 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19452 * @class Roo.bootstrap.Radio
19453 * @extends Roo.bootstrap.CheckBox
19454 * Bootstrap Radio class
19457 * Create a new Radio
19458 * @param {Object} config The config object
19461 Roo.bootstrap.Radio = function(config){
19462 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19466 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19468 inputType: 'radio',
19472 getAutoCreate : function()
19474 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19475 align = align || 'left'; // default...
19482 tag : this.inline ? 'span' : 'div',
19487 var inline = this.inline ? ' radio-inline' : '';
19491 // does not need for, as we wrap the input with it..
19493 cls : 'control-label box-label' + inline,
19496 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19500 //cls : 'control-label' + inline,
19501 html : this.fieldLabel,
19502 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19511 type : this.inputType,
19512 //value : (!this.checked) ? this.valueOff : this.inputValue,
19513 value : this.inputValue,
19515 placeholder : this.placeholder || '' // ?? needed????
19518 if (this.weight) { // Validity check?
19519 input.cls += " radio-" + this.weight;
19521 if (this.disabled) {
19522 input.disabled=true;
19526 input.checked = this.checked;
19530 input.name = this.name;
19534 input.cls += ' input-' + this.size;
19537 //?? can span's inline have a width??
19540 ['xs','sm','md','lg'].map(function(size){
19541 if (settings[size]) {
19542 cfg.cls += ' col-' + size + '-' + settings[size];
19546 var inputblock = input;
19548 if (this.before || this.after) {
19551 cls : 'input-group',
19556 inputblock.cn.push({
19558 cls : 'input-group-addon',
19562 inputblock.cn.push(input);
19564 inputblock.cn.push({
19566 cls : 'input-group-addon',
19574 if (this.fieldLabel && this.fieldLabel.length) {
19575 cfg.cn.push(fieldLabel);
19578 // normal bootstrap puts the input inside the label.
19579 // however with our styled version - it has to go after the input.
19581 //lbl.cn.push(inputblock);
19585 cls: 'radio' + inline,
19592 cfg.cn.push( lblwrap);
19597 html: this.boxLabel
19606 initEvents : function()
19608 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19610 this.inputEl().on('click', this.onClick, this);
19611 if (this.boxLabel) {
19612 //Roo.log('find label');
19613 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19618 inputEl: function ()
19620 return this.el.select('input.roo-radio',true).first();
19622 onClick : function()
19625 this.setChecked(true);
19628 setChecked : function(state,suppressEvent)
19631 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19632 v.dom.checked = false;
19635 Roo.log(this.inputEl().dom);
19636 this.checked = state;
19637 this.inputEl().dom.checked = state;
19639 if(suppressEvent !== true){
19640 this.fireEvent('check', this, state);
19643 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19647 getGroupValue : function()
19650 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19651 if(v.dom.checked == true){
19652 value = v.dom.value;
19660 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19661 * @return {Mixed} value The field value
19663 getValue : function(){
19664 return this.getGroupValue();
19670 //<script type="text/javascript">
19673 * Based Ext JS Library 1.1.1
19674 * Copyright(c) 2006-2007, Ext JS, LLC.
19680 * @class Roo.HtmlEditorCore
19681 * @extends Roo.Component
19682 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19684 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19687 Roo.HtmlEditorCore = function(config){
19690 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19695 * @event initialize
19696 * Fires when the editor is fully initialized (including the iframe)
19697 * @param {Roo.HtmlEditorCore} this
19702 * Fires when the editor is first receives the focus. Any insertion must wait
19703 * until after this event.
19704 * @param {Roo.HtmlEditorCore} this
19708 * @event beforesync
19709 * Fires before the textarea is updated with content from the editor iframe. Return false
19710 * to cancel the sync.
19711 * @param {Roo.HtmlEditorCore} this
19712 * @param {String} html
19716 * @event beforepush
19717 * Fires before the iframe editor is updated with content from the textarea. Return false
19718 * to cancel the push.
19719 * @param {Roo.HtmlEditorCore} this
19720 * @param {String} html
19725 * Fires when the textarea is updated with content from the editor iframe.
19726 * @param {Roo.HtmlEditorCore} this
19727 * @param {String} html
19732 * Fires when the iframe editor is updated with content from the textarea.
19733 * @param {Roo.HtmlEditorCore} this
19734 * @param {String} html
19739 * @event editorevent
19740 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19741 * @param {Roo.HtmlEditorCore} this
19747 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19749 // defaults : white / black...
19750 this.applyBlacklists();
19757 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19761 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19767 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19772 * @cfg {Number} height (in pixels)
19776 * @cfg {Number} width (in pixels)
19781 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19784 stylesheets: false,
19789 // private properties
19790 validationEvent : false,
19792 initialized : false,
19794 sourceEditMode : false,
19795 onFocus : Roo.emptyFn,
19797 hideMode:'offsets',
19801 // blacklist + whitelisted elements..
19808 * Protected method that will not generally be called directly. It
19809 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19810 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19812 getDocMarkup : function(){
19816 // inherit styels from page...??
19817 if (this.stylesheets === false) {
19819 Roo.get(document.head).select('style').each(function(node) {
19820 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19823 Roo.get(document.head).select('link').each(function(node) {
19824 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19827 } else if (!this.stylesheets.length) {
19829 st = '<style type="text/css">' +
19830 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19836 st += '<style type="text/css">' +
19837 'IMG { cursor: pointer } ' +
19841 return '<html><head>' + st +
19842 //<style type="text/css">' +
19843 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19845 ' </head><body class="roo-htmleditor-body"></body></html>';
19849 onRender : function(ct, position)
19852 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19853 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19856 this.el.dom.style.border = '0 none';
19857 this.el.dom.setAttribute('tabIndex', -1);
19858 this.el.addClass('x-hidden hide');
19862 if(Roo.isIE){ // fix IE 1px bogus margin
19863 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19867 this.frameId = Roo.id();
19871 var iframe = this.owner.wrap.createChild({
19873 cls: 'form-control', // bootstrap..
19875 name: this.frameId,
19876 frameBorder : 'no',
19877 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19882 this.iframe = iframe.dom;
19884 this.assignDocWin();
19886 this.doc.designMode = 'on';
19889 this.doc.write(this.getDocMarkup());
19893 var task = { // must defer to wait for browser to be ready
19895 //console.log("run task?" + this.doc.readyState);
19896 this.assignDocWin();
19897 if(this.doc.body || this.doc.readyState == 'complete'){
19899 this.doc.designMode="on";
19903 Roo.TaskMgr.stop(task);
19904 this.initEditor.defer(10, this);
19911 Roo.TaskMgr.start(task);
19916 onResize : function(w, h)
19918 Roo.log('resize: ' +w + ',' + h );
19919 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19923 if(typeof w == 'number'){
19925 this.iframe.style.width = w + 'px';
19927 if(typeof h == 'number'){
19929 this.iframe.style.height = h + 'px';
19931 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19938 * Toggles the editor between standard and source edit mode.
19939 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19941 toggleSourceEdit : function(sourceEditMode){
19943 this.sourceEditMode = sourceEditMode === true;
19945 if(this.sourceEditMode){
19947 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19950 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19951 //this.iframe.className = '';
19954 //this.setSize(this.owner.wrap.getSize());
19955 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19962 * Protected method that will not generally be called directly. If you need/want
19963 * custom HTML cleanup, this is the method you should override.
19964 * @param {String} html The HTML to be cleaned
19965 * return {String} The cleaned HTML
19967 cleanHtml : function(html){
19968 html = String(html);
19969 if(html.length > 5){
19970 if(Roo.isSafari){ // strip safari nonsense
19971 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19974 if(html == ' '){
19981 * HTML Editor -> Textarea
19982 * Protected method that will not generally be called directly. Syncs the contents
19983 * of the editor iframe with the textarea.
19985 syncValue : function(){
19986 if(this.initialized){
19987 var bd = (this.doc.body || this.doc.documentElement);
19988 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19989 var html = bd.innerHTML;
19991 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19992 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19994 html = '<div style="'+m[0]+'">' + html + '</div>';
19997 html = this.cleanHtml(html);
19998 // fix up the special chars.. normaly like back quotes in word...
19999 // however we do not want to do this with chinese..
20000 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20001 var cc = b.charCodeAt();
20003 (cc >= 0x4E00 && cc < 0xA000 ) ||
20004 (cc >= 0x3400 && cc < 0x4E00 ) ||
20005 (cc >= 0xf900 && cc < 0xfb00 )
20011 if(this.owner.fireEvent('beforesync', this, html) !== false){
20012 this.el.dom.value = html;
20013 this.owner.fireEvent('sync', this, html);
20019 * Protected method that will not generally be called directly. Pushes the value of the textarea
20020 * into the iframe editor.
20022 pushValue : function(){
20023 if(this.initialized){
20024 var v = this.el.dom.value.trim();
20026 // if(v.length < 1){
20030 if(this.owner.fireEvent('beforepush', this, v) !== false){
20031 var d = (this.doc.body || this.doc.documentElement);
20033 this.cleanUpPaste();
20034 this.el.dom.value = d.innerHTML;
20035 this.owner.fireEvent('push', this, v);
20041 deferFocus : function(){
20042 this.focus.defer(10, this);
20046 focus : function(){
20047 if(this.win && !this.sourceEditMode){
20054 assignDocWin: function()
20056 var iframe = this.iframe;
20059 this.doc = iframe.contentWindow.document;
20060 this.win = iframe.contentWindow;
20062 // if (!Roo.get(this.frameId)) {
20065 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20066 // this.win = Roo.get(this.frameId).dom.contentWindow;
20068 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20072 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20073 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20078 initEditor : function(){
20079 //console.log("INIT EDITOR");
20080 this.assignDocWin();
20084 this.doc.designMode="on";
20086 this.doc.write(this.getDocMarkup());
20089 var dbody = (this.doc.body || this.doc.documentElement);
20090 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20091 // this copies styles from the containing element into thsi one..
20092 // not sure why we need all of this..
20093 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20095 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20096 //ss['background-attachment'] = 'fixed'; // w3c
20097 dbody.bgProperties = 'fixed'; // ie
20098 //Roo.DomHelper.applyStyles(dbody, ss);
20099 Roo.EventManager.on(this.doc, {
20100 //'mousedown': this.onEditorEvent,
20101 'mouseup': this.onEditorEvent,
20102 'dblclick': this.onEditorEvent,
20103 'click': this.onEditorEvent,
20104 'keyup': this.onEditorEvent,
20109 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20111 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20112 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20114 this.initialized = true;
20116 this.owner.fireEvent('initialize', this);
20121 onDestroy : function(){
20127 //for (var i =0; i < this.toolbars.length;i++) {
20128 // // fixme - ask toolbars for heights?
20129 // this.toolbars[i].onDestroy();
20132 //this.wrap.dom.innerHTML = '';
20133 //this.wrap.remove();
20138 onFirstFocus : function(){
20140 this.assignDocWin();
20143 this.activated = true;
20146 if(Roo.isGecko){ // prevent silly gecko errors
20148 var s = this.win.getSelection();
20149 if(!s.focusNode || s.focusNode.nodeType != 3){
20150 var r = s.getRangeAt(0);
20151 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20156 this.execCmd('useCSS', true);
20157 this.execCmd('styleWithCSS', false);
20160 this.owner.fireEvent('activate', this);
20164 adjustFont: function(btn){
20165 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20166 //if(Roo.isSafari){ // safari
20169 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20170 if(Roo.isSafari){ // safari
20171 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20172 v = (v < 10) ? 10 : v;
20173 v = (v > 48) ? 48 : v;
20174 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20179 v = Math.max(1, v+adjust);
20181 this.execCmd('FontSize', v );
20184 onEditorEvent : function(e)
20186 this.owner.fireEvent('editorevent', this, e);
20187 // this.updateToolbar();
20188 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20191 insertTag : function(tg)
20193 // could be a bit smarter... -> wrap the current selected tRoo..
20194 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20196 range = this.createRange(this.getSelection());
20197 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20198 wrappingNode.appendChild(range.extractContents());
20199 range.insertNode(wrappingNode);
20206 this.execCmd("formatblock", tg);
20210 insertText : function(txt)
20214 var range = this.createRange();
20215 range.deleteContents();
20216 //alert(Sender.getAttribute('label'));
20218 range.insertNode(this.doc.createTextNode(txt));
20224 * Executes a Midas editor command on the editor document and performs necessary focus and
20225 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20226 * @param {String} cmd The Midas command
20227 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20229 relayCmd : function(cmd, value){
20231 this.execCmd(cmd, value);
20232 this.owner.fireEvent('editorevent', this);
20233 //this.updateToolbar();
20234 this.owner.deferFocus();
20238 * Executes a Midas editor command directly on the editor document.
20239 * For visual commands, you should use {@link #relayCmd} instead.
20240 * <b>This should only be called after the editor is initialized.</b>
20241 * @param {String} cmd The Midas command
20242 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20244 execCmd : function(cmd, value){
20245 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20252 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20254 * @param {String} text | dom node..
20256 insertAtCursor : function(text)
20261 if(!this.activated){
20267 var r = this.doc.selection.createRange();
20278 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20282 // from jquery ui (MIT licenced)
20284 var win = this.win;
20286 if (win.getSelection && win.getSelection().getRangeAt) {
20287 range = win.getSelection().getRangeAt(0);
20288 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20289 range.insertNode(node);
20290 } else if (win.document.selection && win.document.selection.createRange) {
20291 // no firefox support
20292 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20293 win.document.selection.createRange().pasteHTML(txt);
20295 // no firefox support
20296 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20297 this.execCmd('InsertHTML', txt);
20306 mozKeyPress : function(e){
20308 var c = e.getCharCode(), cmd;
20311 c = String.fromCharCode(c).toLowerCase();
20325 this.cleanUpPaste.defer(100, this);
20333 e.preventDefault();
20341 fixKeys : function(){ // load time branching for fastest keydown performance
20343 return function(e){
20344 var k = e.getKey(), r;
20347 r = this.doc.selection.createRange();
20350 r.pasteHTML('    ');
20357 r = this.doc.selection.createRange();
20359 var target = r.parentElement();
20360 if(!target || target.tagName.toLowerCase() != 'li'){
20362 r.pasteHTML('<br />');
20368 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20369 this.cleanUpPaste.defer(100, this);
20375 }else if(Roo.isOpera){
20376 return function(e){
20377 var k = e.getKey();
20381 this.execCmd('InsertHTML','    ');
20384 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20385 this.cleanUpPaste.defer(100, this);
20390 }else if(Roo.isSafari){
20391 return function(e){
20392 var k = e.getKey();
20396 this.execCmd('InsertText','\t');
20400 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20401 this.cleanUpPaste.defer(100, this);
20409 getAllAncestors: function()
20411 var p = this.getSelectedNode();
20414 a.push(p); // push blank onto stack..
20415 p = this.getParentElement();
20419 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20423 a.push(this.doc.body);
20427 lastSelNode : false,
20430 getSelection : function()
20432 this.assignDocWin();
20433 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20436 getSelectedNode: function()
20438 // this may only work on Gecko!!!
20440 // should we cache this!!!!
20445 var range = this.createRange(this.getSelection()).cloneRange();
20448 var parent = range.parentElement();
20450 var testRange = range.duplicate();
20451 testRange.moveToElementText(parent);
20452 if (testRange.inRange(range)) {
20455 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20458 parent = parent.parentElement;
20463 // is ancestor a text element.
20464 var ac = range.commonAncestorContainer;
20465 if (ac.nodeType == 3) {
20466 ac = ac.parentNode;
20469 var ar = ac.childNodes;
20472 var other_nodes = [];
20473 var has_other_nodes = false;
20474 for (var i=0;i<ar.length;i++) {
20475 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20478 // fullly contained node.
20480 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20485 // probably selected..
20486 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20487 other_nodes.push(ar[i]);
20491 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20496 has_other_nodes = true;
20498 if (!nodes.length && other_nodes.length) {
20499 nodes= other_nodes;
20501 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20507 createRange: function(sel)
20509 // this has strange effects when using with
20510 // top toolbar - not sure if it's a great idea.
20511 //this.editor.contentWindow.focus();
20512 if (typeof sel != "undefined") {
20514 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20516 return this.doc.createRange();
20519 return this.doc.createRange();
20522 getParentElement: function()
20525 this.assignDocWin();
20526 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20528 var range = this.createRange(sel);
20531 var p = range.commonAncestorContainer;
20532 while (p.nodeType == 3) { // text node
20543 * Range intersection.. the hard stuff...
20547 * [ -- selected range --- ]
20551 * if end is before start or hits it. fail.
20552 * if start is after end or hits it fail.
20554 * if either hits (but other is outside. - then it's not
20560 // @see http://www.thismuchiknow.co.uk/?p=64.
20561 rangeIntersectsNode : function(range, node)
20563 var nodeRange = node.ownerDocument.createRange();
20565 nodeRange.selectNode(node);
20567 nodeRange.selectNodeContents(node);
20570 var rangeStartRange = range.cloneRange();
20571 rangeStartRange.collapse(true);
20573 var rangeEndRange = range.cloneRange();
20574 rangeEndRange.collapse(false);
20576 var nodeStartRange = nodeRange.cloneRange();
20577 nodeStartRange.collapse(true);
20579 var nodeEndRange = nodeRange.cloneRange();
20580 nodeEndRange.collapse(false);
20582 return rangeStartRange.compareBoundaryPoints(
20583 Range.START_TO_START, nodeEndRange) == -1 &&
20584 rangeEndRange.compareBoundaryPoints(
20585 Range.START_TO_START, nodeStartRange) == 1;
20589 rangeCompareNode : function(range, node)
20591 var nodeRange = node.ownerDocument.createRange();
20593 nodeRange.selectNode(node);
20595 nodeRange.selectNodeContents(node);
20599 range.collapse(true);
20601 nodeRange.collapse(true);
20603 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20604 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20606 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20608 var nodeIsBefore = ss == 1;
20609 var nodeIsAfter = ee == -1;
20611 if (nodeIsBefore && nodeIsAfter) {
20614 if (!nodeIsBefore && nodeIsAfter) {
20615 return 1; //right trailed.
20618 if (nodeIsBefore && !nodeIsAfter) {
20619 return 2; // left trailed.
20625 // private? - in a new class?
20626 cleanUpPaste : function()
20628 // cleans up the whole document..
20629 Roo.log('cleanuppaste');
20631 this.cleanUpChildren(this.doc.body);
20632 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20633 if (clean != this.doc.body.innerHTML) {
20634 this.doc.body.innerHTML = clean;
20639 cleanWordChars : function(input) {// change the chars to hex code
20640 var he = Roo.HtmlEditorCore;
20642 var output = input;
20643 Roo.each(he.swapCodes, function(sw) {
20644 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20646 output = output.replace(swapper, sw[1]);
20653 cleanUpChildren : function (n)
20655 if (!n.childNodes.length) {
20658 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20659 this.cleanUpChild(n.childNodes[i]);
20666 cleanUpChild : function (node)
20669 //console.log(node);
20670 if (node.nodeName == "#text") {
20671 // clean up silly Windows -- stuff?
20674 if (node.nodeName == "#comment") {
20675 node.parentNode.removeChild(node);
20676 // clean up silly Windows -- stuff?
20679 var lcname = node.tagName.toLowerCase();
20680 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20681 // whitelist of tags..
20683 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20685 node.parentNode.removeChild(node);
20690 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20692 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20693 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20695 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20696 // remove_keep_children = true;
20699 if (remove_keep_children) {
20700 this.cleanUpChildren(node);
20701 // inserts everything just before this node...
20702 while (node.childNodes.length) {
20703 var cn = node.childNodes[0];
20704 node.removeChild(cn);
20705 node.parentNode.insertBefore(cn, node);
20707 node.parentNode.removeChild(node);
20711 if (!node.attributes || !node.attributes.length) {
20712 this.cleanUpChildren(node);
20716 function cleanAttr(n,v)
20719 if (v.match(/^\./) || v.match(/^\//)) {
20722 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20725 if (v.match(/^#/)) {
20728 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20729 node.removeAttribute(n);
20733 var cwhite = this.cwhite;
20734 var cblack = this.cblack;
20736 function cleanStyle(n,v)
20738 if (v.match(/expression/)) { //XSS?? should we even bother..
20739 node.removeAttribute(n);
20743 var parts = v.split(/;/);
20746 Roo.each(parts, function(p) {
20747 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20751 var l = p.split(':').shift().replace(/\s+/g,'');
20752 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20754 if ( cwhite.length && cblack.indexOf(l) > -1) {
20755 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20756 //node.removeAttribute(n);
20760 // only allow 'c whitelisted system attributes'
20761 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20762 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20763 //node.removeAttribute(n);
20773 if (clean.length) {
20774 node.setAttribute(n, clean.join(';'));
20776 node.removeAttribute(n);
20782 for (var i = node.attributes.length-1; i > -1 ; i--) {
20783 var a = node.attributes[i];
20786 if (a.name.toLowerCase().substr(0,2)=='on') {
20787 node.removeAttribute(a.name);
20790 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20791 node.removeAttribute(a.name);
20794 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20795 cleanAttr(a.name,a.value); // fixme..
20798 if (a.name == 'style') {
20799 cleanStyle(a.name,a.value);
20802 /// clean up MS crap..
20803 // tecnically this should be a list of valid class'es..
20806 if (a.name == 'class') {
20807 if (a.value.match(/^Mso/)) {
20808 node.className = '';
20811 if (a.value.match(/body/)) {
20812 node.className = '';
20823 this.cleanUpChildren(node);
20829 * Clean up MS wordisms...
20831 cleanWord : function(node)
20836 this.cleanWord(this.doc.body);
20839 if (node.nodeName == "#text") {
20840 // clean up silly Windows -- stuff?
20843 if (node.nodeName == "#comment") {
20844 node.parentNode.removeChild(node);
20845 // clean up silly Windows -- stuff?
20849 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20850 node.parentNode.removeChild(node);
20854 // remove - but keep children..
20855 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20856 while (node.childNodes.length) {
20857 var cn = node.childNodes[0];
20858 node.removeChild(cn);
20859 node.parentNode.insertBefore(cn, node);
20861 node.parentNode.removeChild(node);
20862 this.iterateChildren(node, this.cleanWord);
20866 if (node.className.length) {
20868 var cn = node.className.split(/\W+/);
20870 Roo.each(cn, function(cls) {
20871 if (cls.match(/Mso[a-zA-Z]+/)) {
20876 node.className = cna.length ? cna.join(' ') : '';
20878 node.removeAttribute("class");
20882 if (node.hasAttribute("lang")) {
20883 node.removeAttribute("lang");
20886 if (node.hasAttribute("style")) {
20888 var styles = node.getAttribute("style").split(";");
20890 Roo.each(styles, function(s) {
20891 if (!s.match(/:/)) {
20894 var kv = s.split(":");
20895 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20898 // what ever is left... we allow.
20901 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20902 if (!nstyle.length) {
20903 node.removeAttribute('style');
20906 this.iterateChildren(node, this.cleanWord);
20912 * iterateChildren of a Node, calling fn each time, using this as the scole..
20913 * @param {DomNode} node node to iterate children of.
20914 * @param {Function} fn method of this class to call on each item.
20916 iterateChildren : function(node, fn)
20918 if (!node.childNodes.length) {
20921 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20922 fn.call(this, node.childNodes[i])
20928 * cleanTableWidths.
20930 * Quite often pasting from word etc.. results in tables with column and widths.
20931 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20934 cleanTableWidths : function(node)
20939 this.cleanTableWidths(this.doc.body);
20944 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20947 Roo.log(node.tagName);
20948 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20949 this.iterateChildren(node, this.cleanTableWidths);
20952 if (node.hasAttribute('width')) {
20953 node.removeAttribute('width');
20957 if (node.hasAttribute("style")) {
20960 var styles = node.getAttribute("style").split(";");
20962 Roo.each(styles, function(s) {
20963 if (!s.match(/:/)) {
20966 var kv = s.split(":");
20967 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20970 // what ever is left... we allow.
20973 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20974 if (!nstyle.length) {
20975 node.removeAttribute('style');
20979 this.iterateChildren(node, this.cleanTableWidths);
20987 domToHTML : function(currentElement, depth, nopadtext) {
20989 depth = depth || 0;
20990 nopadtext = nopadtext || false;
20992 if (!currentElement) {
20993 return this.domToHTML(this.doc.body);
20996 //Roo.log(currentElement);
20998 var allText = false;
20999 var nodeName = currentElement.nodeName;
21000 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21002 if (nodeName == '#text') {
21004 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21009 if (nodeName != 'BODY') {
21012 // Prints the node tagName, such as <A>, <IMG>, etc
21015 for(i = 0; i < currentElement.attributes.length;i++) {
21017 var aname = currentElement.attributes.item(i).name;
21018 if (!currentElement.attributes.item(i).value.length) {
21021 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21024 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21033 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21036 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21041 // Traverse the tree
21043 var currentElementChild = currentElement.childNodes.item(i);
21044 var allText = true;
21045 var innerHTML = '';
21047 while (currentElementChild) {
21048 // Formatting code (indent the tree so it looks nice on the screen)
21049 var nopad = nopadtext;
21050 if (lastnode == 'SPAN') {
21054 if (currentElementChild.nodeName == '#text') {
21055 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21056 toadd = nopadtext ? toadd : toadd.trim();
21057 if (!nopad && toadd.length > 80) {
21058 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21060 innerHTML += toadd;
21063 currentElementChild = currentElement.childNodes.item(i);
21069 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21071 // Recursively traverse the tree structure of the child node
21072 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21073 lastnode = currentElementChild.nodeName;
21075 currentElementChild=currentElement.childNodes.item(i);
21081 // The remaining code is mostly for formatting the tree
21082 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21087 ret+= "</"+tagName+">";
21093 applyBlacklists : function()
21095 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21096 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21100 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21101 if (b.indexOf(tag) > -1) {
21104 this.white.push(tag);
21108 Roo.each(w, function(tag) {
21109 if (b.indexOf(tag) > -1) {
21112 if (this.white.indexOf(tag) > -1) {
21115 this.white.push(tag);
21120 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21121 if (w.indexOf(tag) > -1) {
21124 this.black.push(tag);
21128 Roo.each(b, function(tag) {
21129 if (w.indexOf(tag) > -1) {
21132 if (this.black.indexOf(tag) > -1) {
21135 this.black.push(tag);
21140 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21141 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21145 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21146 if (b.indexOf(tag) > -1) {
21149 this.cwhite.push(tag);
21153 Roo.each(w, function(tag) {
21154 if (b.indexOf(tag) > -1) {
21157 if (this.cwhite.indexOf(tag) > -1) {
21160 this.cwhite.push(tag);
21165 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21166 if (w.indexOf(tag) > -1) {
21169 this.cblack.push(tag);
21173 Roo.each(b, function(tag) {
21174 if (w.indexOf(tag) > -1) {
21177 if (this.cblack.indexOf(tag) > -1) {
21180 this.cblack.push(tag);
21185 setStylesheets : function(stylesheets)
21187 if(typeof(stylesheets) == 'string'){
21188 Roo.get(this.iframe.contentDocument.head).createChild({
21190 rel : 'stylesheet',
21199 Roo.each(stylesheets, function(s) {
21204 Roo.get(_this.iframe.contentDocument.head).createChild({
21206 rel : 'stylesheet',
21215 removeStylesheets : function()
21219 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21224 // hide stuff that is not compatible
21238 * @event specialkey
21242 * @cfg {String} fieldClass @hide
21245 * @cfg {String} focusClass @hide
21248 * @cfg {String} autoCreate @hide
21251 * @cfg {String} inputType @hide
21254 * @cfg {String} invalidClass @hide
21257 * @cfg {String} invalidText @hide
21260 * @cfg {String} msgFx @hide
21263 * @cfg {String} validateOnBlur @hide
21267 Roo.HtmlEditorCore.white = [
21268 'area', 'br', 'img', 'input', 'hr', 'wbr',
21270 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21271 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21272 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21273 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21274 'table', 'ul', 'xmp',
21276 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21279 'dir', 'menu', 'ol', 'ul', 'dl',
21285 Roo.HtmlEditorCore.black = [
21286 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21288 'base', 'basefont', 'bgsound', 'blink', 'body',
21289 'frame', 'frameset', 'head', 'html', 'ilayer',
21290 'iframe', 'layer', 'link', 'meta', 'object',
21291 'script', 'style' ,'title', 'xml' // clean later..
21293 Roo.HtmlEditorCore.clean = [
21294 'script', 'style', 'title', 'xml'
21296 Roo.HtmlEditorCore.remove = [
21301 Roo.HtmlEditorCore.ablack = [
21305 Roo.HtmlEditorCore.aclean = [
21306 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21310 Roo.HtmlEditorCore.pwhite= [
21311 'http', 'https', 'mailto'
21314 // white listed style attributes.
21315 Roo.HtmlEditorCore.cwhite= [
21316 // 'text-align', /// default is to allow most things..
21322 // black listed style attributes.
21323 Roo.HtmlEditorCore.cblack= [
21324 // 'font-size' -- this can be set by the project
21328 Roo.HtmlEditorCore.swapCodes =[
21347 * @class Roo.bootstrap.HtmlEditor
21348 * @extends Roo.bootstrap.TextArea
21349 * Bootstrap HtmlEditor class
21352 * Create a new HtmlEditor
21353 * @param {Object} config The config object
21356 Roo.bootstrap.HtmlEditor = function(config){
21357 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21358 if (!this.toolbars) {
21359 this.toolbars = [];
21361 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21364 * @event initialize
21365 * Fires when the editor is fully initialized (including the iframe)
21366 * @param {HtmlEditor} this
21371 * Fires when the editor is first receives the focus. Any insertion must wait
21372 * until after this event.
21373 * @param {HtmlEditor} this
21377 * @event beforesync
21378 * Fires before the textarea is updated with content from the editor iframe. Return false
21379 * to cancel the sync.
21380 * @param {HtmlEditor} this
21381 * @param {String} html
21385 * @event beforepush
21386 * Fires before the iframe editor is updated with content from the textarea. Return false
21387 * to cancel the push.
21388 * @param {HtmlEditor} this
21389 * @param {String} html
21394 * Fires when the textarea is updated with content from the editor iframe.
21395 * @param {HtmlEditor} this
21396 * @param {String} html
21401 * Fires when the iframe editor is updated with content from the textarea.
21402 * @param {HtmlEditor} this
21403 * @param {String} html
21407 * @event editmodechange
21408 * Fires when the editor switches edit modes
21409 * @param {HtmlEditor} this
21410 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21412 editmodechange: true,
21414 * @event editorevent
21415 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21416 * @param {HtmlEditor} this
21420 * @event firstfocus
21421 * Fires when on first focus - needed by toolbars..
21422 * @param {HtmlEditor} this
21427 * Auto save the htmlEditor value as a file into Events
21428 * @param {HtmlEditor} this
21432 * @event savedpreview
21433 * preview the saved version of htmlEditor
21434 * @param {HtmlEditor} this
21441 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21445 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21450 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21455 * @cfg {Number} height (in pixels)
21459 * @cfg {Number} width (in pixels)
21464 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21467 stylesheets: false,
21472 // private properties
21473 validationEvent : false,
21475 initialized : false,
21478 onFocus : Roo.emptyFn,
21480 hideMode:'offsets',
21483 tbContainer : false,
21485 toolbarContainer :function() {
21486 return this.wrap.select('.x-html-editor-tb',true).first();
21490 * Protected method that will not generally be called directly. It
21491 * is called when the editor creates its toolbar. Override this method if you need to
21492 * add custom toolbar buttons.
21493 * @param {HtmlEditor} editor
21495 createToolbar : function(){
21497 Roo.log("create toolbars");
21499 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21500 this.toolbars[0].render(this.toolbarContainer());
21504 // if (!editor.toolbars || !editor.toolbars.length) {
21505 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21508 // for (var i =0 ; i < editor.toolbars.length;i++) {
21509 // editor.toolbars[i] = Roo.factory(
21510 // typeof(editor.toolbars[i]) == 'string' ?
21511 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21512 // Roo.bootstrap.HtmlEditor);
21513 // editor.toolbars[i].init(editor);
21519 onRender : function(ct, position)
21521 // Roo.log("Call onRender: " + this.xtype);
21523 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21525 this.wrap = this.inputEl().wrap({
21526 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21529 this.editorcore.onRender(ct, position);
21531 if (this.resizable) {
21532 this.resizeEl = new Roo.Resizable(this.wrap, {
21536 minHeight : this.height,
21537 height: this.height,
21538 handles : this.resizable,
21541 resize : function(r, w, h) {
21542 _t.onResize(w,h); // -something
21548 this.createToolbar(this);
21551 if(!this.width && this.resizable){
21552 this.setSize(this.wrap.getSize());
21554 if (this.resizeEl) {
21555 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21556 // should trigger onReize..
21562 onResize : function(w, h)
21564 Roo.log('resize: ' +w + ',' + h );
21565 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21569 if(this.inputEl() ){
21570 if(typeof w == 'number'){
21571 var aw = w - this.wrap.getFrameWidth('lr');
21572 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21575 if(typeof h == 'number'){
21576 var tbh = -11; // fixme it needs to tool bar size!
21577 for (var i =0; i < this.toolbars.length;i++) {
21578 // fixme - ask toolbars for heights?
21579 tbh += this.toolbars[i].el.getHeight();
21580 //if (this.toolbars[i].footer) {
21581 // tbh += this.toolbars[i].footer.el.getHeight();
21589 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21590 ah -= 5; // knock a few pixes off for look..
21591 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21595 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21596 this.editorcore.onResize(ew,eh);
21601 * Toggles the editor between standard and source edit mode.
21602 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21604 toggleSourceEdit : function(sourceEditMode)
21606 this.editorcore.toggleSourceEdit(sourceEditMode);
21608 if(this.editorcore.sourceEditMode){
21609 Roo.log('editor - showing textarea');
21612 // Roo.log(this.syncValue());
21614 this.inputEl().removeClass(['hide', 'x-hidden']);
21615 this.inputEl().dom.removeAttribute('tabIndex');
21616 this.inputEl().focus();
21618 Roo.log('editor - hiding textarea');
21620 // Roo.log(this.pushValue());
21623 this.inputEl().addClass(['hide', 'x-hidden']);
21624 this.inputEl().dom.setAttribute('tabIndex', -1);
21625 //this.deferFocus();
21628 if(this.resizable){
21629 this.setSize(this.wrap.getSize());
21632 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21635 // private (for BoxComponent)
21636 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21638 // private (for BoxComponent)
21639 getResizeEl : function(){
21643 // private (for BoxComponent)
21644 getPositionEl : function(){
21649 initEvents : function(){
21650 this.originalValue = this.getValue();
21654 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21657 // markInvalid : Roo.emptyFn,
21659 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21662 // clearInvalid : Roo.emptyFn,
21664 setValue : function(v){
21665 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21666 this.editorcore.pushValue();
21671 deferFocus : function(){
21672 this.focus.defer(10, this);
21676 focus : function(){
21677 this.editorcore.focus();
21683 onDestroy : function(){
21689 for (var i =0; i < this.toolbars.length;i++) {
21690 // fixme - ask toolbars for heights?
21691 this.toolbars[i].onDestroy();
21694 this.wrap.dom.innerHTML = '';
21695 this.wrap.remove();
21700 onFirstFocus : function(){
21701 //Roo.log("onFirstFocus");
21702 this.editorcore.onFirstFocus();
21703 for (var i =0; i < this.toolbars.length;i++) {
21704 this.toolbars[i].onFirstFocus();
21710 syncValue : function()
21712 this.editorcore.syncValue();
21715 pushValue : function()
21717 this.editorcore.pushValue();
21721 // hide stuff that is not compatible
21735 * @event specialkey
21739 * @cfg {String} fieldClass @hide
21742 * @cfg {String} focusClass @hide
21745 * @cfg {String} autoCreate @hide
21748 * @cfg {String} inputType @hide
21751 * @cfg {String} invalidClass @hide
21754 * @cfg {String} invalidText @hide
21757 * @cfg {String} msgFx @hide
21760 * @cfg {String} validateOnBlur @hide
21769 Roo.namespace('Roo.bootstrap.htmleditor');
21771 * @class Roo.bootstrap.HtmlEditorToolbar1
21776 new Roo.bootstrap.HtmlEditor({
21779 new Roo.bootstrap.HtmlEditorToolbar1({
21780 disable : { fonts: 1 , format: 1, ..., ... , ...],
21786 * @cfg {Object} disable List of elements to disable..
21787 * @cfg {Array} btns List of additional buttons.
21791 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21794 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21797 Roo.apply(this, config);
21799 // default disabled, based on 'good practice'..
21800 this.disable = this.disable || {};
21801 Roo.applyIf(this.disable, {
21804 specialElements : true
21806 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21808 this.editor = config.editor;
21809 this.editorcore = config.editor.editorcore;
21811 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21813 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21814 // dont call parent... till later.
21816 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21821 editorcore : false,
21826 "h1","h2","h3","h4","h5","h6",
21828 "abbr", "acronym", "address", "cite", "samp", "var",
21832 onRender : function(ct, position)
21834 // Roo.log("Call onRender: " + this.xtype);
21836 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21838 this.el.dom.style.marginBottom = '0';
21840 var editorcore = this.editorcore;
21841 var editor= this.editor;
21844 var btn = function(id,cmd , toggle, handler){
21846 var event = toggle ? 'toggle' : 'click';
21851 xns: Roo.bootstrap,
21854 enableToggle:toggle !== false,
21856 pressed : toggle ? false : null,
21859 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21860 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21869 xns: Roo.bootstrap,
21870 glyphicon : 'font',
21874 xns: Roo.bootstrap,
21878 Roo.each(this.formats, function(f) {
21879 style.menu.items.push({
21881 xns: Roo.bootstrap,
21882 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21887 editorcore.insertTag(this.tagname);
21894 children.push(style);
21897 btn('bold',false,true);
21898 btn('italic',false,true);
21899 btn('align-left', 'justifyleft',true);
21900 btn('align-center', 'justifycenter',true);
21901 btn('align-right' , 'justifyright',true);
21902 btn('link', false, false, function(btn) {
21903 //Roo.log("create link?");
21904 var url = prompt(this.createLinkText, this.defaultLinkValue);
21905 if(url && url != 'http:/'+'/'){
21906 this.editorcore.relayCmd('createlink', url);
21909 btn('list','insertunorderedlist',true);
21910 btn('pencil', false,true, function(btn){
21913 this.toggleSourceEdit(btn.pressed);
21919 xns: Roo.bootstrap,
21924 xns: Roo.bootstrap,
21929 cog.menu.items.push({
21931 xns: Roo.bootstrap,
21932 html : Clean styles,
21937 editorcore.insertTag(this.tagname);
21946 this.xtype = 'NavSimplebar';
21948 for(var i=0;i< children.length;i++) {
21950 this.buttons.add(this.addxtypeChild(children[i]));
21954 editor.on('editorevent', this.updateToolbar, this);
21956 onBtnClick : function(id)
21958 this.editorcore.relayCmd(id);
21959 this.editorcore.focus();
21963 * Protected method that will not generally be called directly. It triggers
21964 * a toolbar update by reading the markup state of the current selection in the editor.
21966 updateToolbar: function(){
21968 if(!this.editorcore.activated){
21969 this.editor.onFirstFocus(); // is this neeed?
21973 var btns = this.buttons;
21974 var doc = this.editorcore.doc;
21975 btns.get('bold').setActive(doc.queryCommandState('bold'));
21976 btns.get('italic').setActive(doc.queryCommandState('italic'));
21977 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21979 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21980 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21981 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21983 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21984 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21987 var ans = this.editorcore.getAllAncestors();
21988 if (this.formatCombo) {
21991 var store = this.formatCombo.store;
21992 this.formatCombo.setValue("");
21993 for (var i =0; i < ans.length;i++) {
21994 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21996 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22004 // hides menus... - so this cant be on a menu...
22005 Roo.bootstrap.MenuMgr.hideAll();
22007 Roo.bootstrap.MenuMgr.hideAll();
22008 //this.editorsyncValue();
22010 onFirstFocus: function() {
22011 this.buttons.each(function(item){
22015 toggleSourceEdit : function(sourceEditMode){
22018 if(sourceEditMode){
22019 Roo.log("disabling buttons");
22020 this.buttons.each( function(item){
22021 if(item.cmd != 'pencil'){
22027 Roo.log("enabling buttons");
22028 if(this.editorcore.initialized){
22029 this.buttons.each( function(item){
22035 Roo.log("calling toggole on editor");
22036 // tell the editor that it's been pressed..
22037 this.editor.toggleSourceEdit(sourceEditMode);
22047 * @class Roo.bootstrap.Table.AbstractSelectionModel
22048 * @extends Roo.util.Observable
22049 * Abstract base class for grid SelectionModels. It provides the interface that should be
22050 * implemented by descendant classes. This class should not be directly instantiated.
22053 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22054 this.locked = false;
22055 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22059 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22060 /** @ignore Called by the grid automatically. Do not call directly. */
22061 init : function(grid){
22067 * Locks the selections.
22070 this.locked = true;
22074 * Unlocks the selections.
22076 unlock : function(){
22077 this.locked = false;
22081 * Returns true if the selections are locked.
22082 * @return {Boolean}
22084 isLocked : function(){
22085 return this.locked;
22089 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22090 * @class Roo.bootstrap.Table.RowSelectionModel
22091 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22092 * It supports multiple selections and keyboard selection/navigation.
22094 * @param {Object} config
22097 Roo.bootstrap.Table.RowSelectionModel = function(config){
22098 Roo.apply(this, config);
22099 this.selections = new Roo.util.MixedCollection(false, function(o){
22104 this.lastActive = false;
22108 * @event selectionchange
22109 * Fires when the selection changes
22110 * @param {SelectionModel} this
22112 "selectionchange" : true,
22114 * @event afterselectionchange
22115 * Fires after the selection changes (eg. by key press or clicking)
22116 * @param {SelectionModel} this
22118 "afterselectionchange" : true,
22120 * @event beforerowselect
22121 * Fires when a row is selected being selected, return false to cancel.
22122 * @param {SelectionModel} this
22123 * @param {Number} rowIndex The selected index
22124 * @param {Boolean} keepExisting False if other selections will be cleared
22126 "beforerowselect" : true,
22129 * Fires when a row is selected.
22130 * @param {SelectionModel} this
22131 * @param {Number} rowIndex The selected index
22132 * @param {Roo.data.Record} r The record
22134 "rowselect" : true,
22136 * @event rowdeselect
22137 * Fires when a row is deselected.
22138 * @param {SelectionModel} this
22139 * @param {Number} rowIndex The selected index
22141 "rowdeselect" : true
22143 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22144 this.locked = false;
22147 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22149 * @cfg {Boolean} singleSelect
22150 * True to allow selection of only one row at a time (defaults to false)
22152 singleSelect : false,
22155 initEvents : function(){
22157 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22158 this.grid.on("mousedown", this.handleMouseDown, this);
22159 }else{ // allow click to work like normal
22160 this.grid.on("rowclick", this.handleDragableRowClick, this);
22163 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22164 "up" : function(e){
22166 this.selectPrevious(e.shiftKey);
22167 }else if(this.last !== false && this.lastActive !== false){
22168 var last = this.last;
22169 this.selectRange(this.last, this.lastActive-1);
22170 this.grid.getView().focusRow(this.lastActive);
22171 if(last !== false){
22175 this.selectFirstRow();
22177 this.fireEvent("afterselectionchange", this);
22179 "down" : function(e){
22181 this.selectNext(e.shiftKey);
22182 }else if(this.last !== false && this.lastActive !== false){
22183 var last = this.last;
22184 this.selectRange(this.last, this.lastActive+1);
22185 this.grid.getView().focusRow(this.lastActive);
22186 if(last !== false){
22190 this.selectFirstRow();
22192 this.fireEvent("afterselectionchange", this);
22197 var view = this.grid.view;
22198 view.on("refresh", this.onRefresh, this);
22199 view.on("rowupdated", this.onRowUpdated, this);
22200 view.on("rowremoved", this.onRemove, this);
22204 onRefresh : function(){
22205 var ds = this.grid.dataSource, i, v = this.grid.view;
22206 var s = this.selections;
22207 s.each(function(r){
22208 if((i = ds.indexOfId(r.id)) != -1){
22217 onRemove : function(v, index, r){
22218 this.selections.remove(r);
22222 onRowUpdated : function(v, index, r){
22223 if(this.isSelected(r)){
22224 v.onRowSelect(index);
22230 * @param {Array} records The records to select
22231 * @param {Boolean} keepExisting (optional) True to keep existing selections
22233 selectRecords : function(records, keepExisting){
22235 this.clearSelections();
22237 var ds = this.grid.dataSource;
22238 for(var i = 0, len = records.length; i < len; i++){
22239 this.selectRow(ds.indexOf(records[i]), true);
22244 * Gets the number of selected rows.
22247 getCount : function(){
22248 return this.selections.length;
22252 * Selects the first row in the grid.
22254 selectFirstRow : function(){
22259 * Select the last row.
22260 * @param {Boolean} keepExisting (optional) True to keep existing selections
22262 selectLastRow : function(keepExisting){
22263 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22267 * Selects the row immediately following the last selected row.
22268 * @param {Boolean} keepExisting (optional) True to keep existing selections
22270 selectNext : function(keepExisting){
22271 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22272 this.selectRow(this.last+1, keepExisting);
22273 this.grid.getView().focusRow(this.last);
22278 * Selects the row that precedes the last selected row.
22279 * @param {Boolean} keepExisting (optional) True to keep existing selections
22281 selectPrevious : function(keepExisting){
22283 this.selectRow(this.last-1, keepExisting);
22284 this.grid.getView().focusRow(this.last);
22289 * Returns the selected records
22290 * @return {Array} Array of selected records
22292 getSelections : function(){
22293 return [].concat(this.selections.items);
22297 * Returns the first selected record.
22300 getSelected : function(){
22301 return this.selections.itemAt(0);
22306 * Clears all selections.
22308 clearSelections : function(fast){
22313 var ds = this.grid.dataSource;
22314 var s = this.selections;
22315 s.each(function(r){
22316 this.deselectRow(ds.indexOfId(r.id));
22320 this.selections.clear();
22327 * Selects all rows.
22329 selectAll : function(){
22333 this.selections.clear();
22334 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22335 this.selectRow(i, true);
22340 * Returns True if there is a selection.
22341 * @return {Boolean}
22343 hasSelection : function(){
22344 return this.selections.length > 0;
22348 * Returns True if the specified row is selected.
22349 * @param {Number/Record} record The record or index of the record to check
22350 * @return {Boolean}
22352 isSelected : function(index){
22353 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22354 return (r && this.selections.key(r.id) ? true : false);
22358 * Returns True if the specified record id is selected.
22359 * @param {String} id The id of record to check
22360 * @return {Boolean}
22362 isIdSelected : function(id){
22363 return (this.selections.key(id) ? true : false);
22367 handleMouseDown : function(e, t){
22368 var view = this.grid.getView(), rowIndex;
22369 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22372 if(e.shiftKey && this.last !== false){
22373 var last = this.last;
22374 this.selectRange(last, rowIndex, e.ctrlKey);
22375 this.last = last; // reset the last
22376 view.focusRow(rowIndex);
22378 var isSelected = this.isSelected(rowIndex);
22379 if(e.button !== 0 && isSelected){
22380 view.focusRow(rowIndex);
22381 }else if(e.ctrlKey && isSelected){
22382 this.deselectRow(rowIndex);
22383 }else if(!isSelected){
22384 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22385 view.focusRow(rowIndex);
22388 this.fireEvent("afterselectionchange", this);
22391 handleDragableRowClick : function(grid, rowIndex, e)
22393 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22394 this.selectRow(rowIndex, false);
22395 grid.view.focusRow(rowIndex);
22396 this.fireEvent("afterselectionchange", this);
22401 * Selects multiple rows.
22402 * @param {Array} rows Array of the indexes of the row to select
22403 * @param {Boolean} keepExisting (optional) True to keep existing selections
22405 selectRows : function(rows, keepExisting){
22407 this.clearSelections();
22409 for(var i = 0, len = rows.length; i < len; i++){
22410 this.selectRow(rows[i], true);
22415 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22416 * @param {Number} startRow The index of the first row in the range
22417 * @param {Number} endRow The index of the last row in the range
22418 * @param {Boolean} keepExisting (optional) True to retain existing selections
22420 selectRange : function(startRow, endRow, keepExisting){
22425 this.clearSelections();
22427 if(startRow <= endRow){
22428 for(var i = startRow; i <= endRow; i++){
22429 this.selectRow(i, true);
22432 for(var i = startRow; i >= endRow; i--){
22433 this.selectRow(i, true);
22439 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22440 * @param {Number} startRow The index of the first row in the range
22441 * @param {Number} endRow The index of the last row in the range
22443 deselectRange : function(startRow, endRow, preventViewNotify){
22447 for(var i = startRow; i <= endRow; i++){
22448 this.deselectRow(i, preventViewNotify);
22454 * @param {Number} row The index of the row to select
22455 * @param {Boolean} keepExisting (optional) True to keep existing selections
22457 selectRow : function(index, keepExisting, preventViewNotify){
22458 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22461 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22462 if(!keepExisting || this.singleSelect){
22463 this.clearSelections();
22465 var r = this.grid.dataSource.getAt(index);
22466 this.selections.add(r);
22467 this.last = this.lastActive = index;
22468 if(!preventViewNotify){
22469 this.grid.getView().onRowSelect(index);
22471 this.fireEvent("rowselect", this, index, r);
22472 this.fireEvent("selectionchange", this);
22478 * @param {Number} row The index of the row to deselect
22480 deselectRow : function(index, preventViewNotify){
22484 if(this.last == index){
22487 if(this.lastActive == index){
22488 this.lastActive = false;
22490 var r = this.grid.dataSource.getAt(index);
22491 this.selections.remove(r);
22492 if(!preventViewNotify){
22493 this.grid.getView().onRowDeselect(index);
22495 this.fireEvent("rowdeselect", this, index);
22496 this.fireEvent("selectionchange", this);
22500 restoreLast : function(){
22502 this.last = this._last;
22507 acceptsNav : function(row, col, cm){
22508 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22512 onEditorKey : function(field, e){
22513 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22518 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22520 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22522 }else if(k == e.ENTER && !e.ctrlKey){
22526 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22528 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22530 }else if(k == e.ESC){
22534 g.startEditing(newCell[0], newCell[1]);
22539 * Ext JS Library 1.1.1
22540 * Copyright(c) 2006-2007, Ext JS, LLC.
22542 * Originally Released Under LGPL - original licence link has changed is not relivant.
22545 * <script type="text/javascript">
22549 * @class Roo.bootstrap.PagingToolbar
22550 * @extends Roo.bootstrap.NavSimplebar
22551 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22553 * Create a new PagingToolbar
22554 * @param {Object} config The config object
22555 * @param {Roo.data.Store} store
22557 Roo.bootstrap.PagingToolbar = function(config)
22559 // old args format still supported... - xtype is prefered..
22560 // created from xtype...
22562 this.ds = config.dataSource;
22564 if (config.store && !this.ds) {
22565 this.store= Roo.factory(config.store, Roo.data);
22566 this.ds = this.store;
22567 this.ds.xmodule = this.xmodule || false;
22570 this.toolbarItems = [];
22571 if (config.items) {
22572 this.toolbarItems = config.items;
22575 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22580 this.bind(this.ds);
22583 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22587 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22589 * @cfg {Roo.data.Store} dataSource
22590 * The underlying data store providing the paged data
22593 * @cfg {String/HTMLElement/Element} container
22594 * container The id or element that will contain the toolbar
22597 * @cfg {Boolean} displayInfo
22598 * True to display the displayMsg (defaults to false)
22601 * @cfg {Number} pageSize
22602 * The number of records to display per page (defaults to 20)
22606 * @cfg {String} displayMsg
22607 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22609 displayMsg : 'Displaying {0} - {1} of {2}',
22611 * @cfg {String} emptyMsg
22612 * The message to display when no records are found (defaults to "No data to display")
22614 emptyMsg : 'No data to display',
22616 * Customizable piece of the default paging text (defaults to "Page")
22619 beforePageText : "Page",
22621 * Customizable piece of the default paging text (defaults to "of %0")
22624 afterPageText : "of {0}",
22626 * Customizable piece of the default paging text (defaults to "First Page")
22629 firstText : "First Page",
22631 * Customizable piece of the default paging text (defaults to "Previous Page")
22634 prevText : "Previous Page",
22636 * Customizable piece of the default paging text (defaults to "Next Page")
22639 nextText : "Next Page",
22641 * Customizable piece of the default paging text (defaults to "Last Page")
22644 lastText : "Last Page",
22646 * Customizable piece of the default paging text (defaults to "Refresh")
22649 refreshText : "Refresh",
22653 onRender : function(ct, position)
22655 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22656 this.navgroup.parentId = this.id;
22657 this.navgroup.onRender(this.el, null);
22658 // add the buttons to the navgroup
22660 if(this.displayInfo){
22661 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22662 this.displayEl = this.el.select('.x-paging-info', true).first();
22663 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22664 // this.displayEl = navel.el.select('span',true).first();
22670 Roo.each(_this.buttons, function(e){ // this might need to use render????
22671 Roo.factory(e).onRender(_this.el, null);
22675 Roo.each(_this.toolbarItems, function(e) {
22676 _this.navgroup.addItem(e);
22680 this.first = this.navgroup.addItem({
22681 tooltip: this.firstText,
22683 icon : 'fa fa-backward',
22685 preventDefault: true,
22686 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22689 this.prev = this.navgroup.addItem({
22690 tooltip: this.prevText,
22692 icon : 'fa fa-step-backward',
22694 preventDefault: true,
22695 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22697 //this.addSeparator();
22700 var field = this.navgroup.addItem( {
22702 cls : 'x-paging-position',
22704 html : this.beforePageText +
22705 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22706 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22709 this.field = field.el.select('input', true).first();
22710 this.field.on("keydown", this.onPagingKeydown, this);
22711 this.field.on("focus", function(){this.dom.select();});
22714 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22715 //this.field.setHeight(18);
22716 //this.addSeparator();
22717 this.next = this.navgroup.addItem({
22718 tooltip: this.nextText,
22720 html : ' <i class="fa fa-step-forward">',
22722 preventDefault: true,
22723 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22725 this.last = this.navgroup.addItem({
22726 tooltip: this.lastText,
22727 icon : 'fa fa-forward',
22730 preventDefault: true,
22731 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22733 //this.addSeparator();
22734 this.loading = this.navgroup.addItem({
22735 tooltip: this.refreshText,
22736 icon: 'fa fa-refresh',
22737 preventDefault: true,
22738 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22744 updateInfo : function(){
22745 if(this.displayEl){
22746 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22747 var msg = count == 0 ?
22751 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22753 this.displayEl.update(msg);
22758 onLoad : function(ds, r, o){
22759 this.cursor = o.params ? o.params.start : 0;
22760 var d = this.getPageData(),
22764 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22765 this.field.dom.value = ap;
22766 this.first.setDisabled(ap == 1);
22767 this.prev.setDisabled(ap == 1);
22768 this.next.setDisabled(ap == ps);
22769 this.last.setDisabled(ap == ps);
22770 this.loading.enable();
22775 getPageData : function(){
22776 var total = this.ds.getTotalCount();
22779 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22780 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22785 onLoadError : function(){
22786 this.loading.enable();
22790 onPagingKeydown : function(e){
22791 var k = e.getKey();
22792 var d = this.getPageData();
22794 var v = this.field.dom.value, pageNum;
22795 if(!v || isNaN(pageNum = parseInt(v, 10))){
22796 this.field.dom.value = d.activePage;
22799 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22800 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22803 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))
22805 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22806 this.field.dom.value = pageNum;
22807 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22810 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22812 var v = this.field.dom.value, pageNum;
22813 var increment = (e.shiftKey) ? 10 : 1;
22814 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22817 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22818 this.field.dom.value = d.activePage;
22821 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22823 this.field.dom.value = parseInt(v, 10) + increment;
22824 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22825 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22832 beforeLoad : function(){
22834 this.loading.disable();
22839 onClick : function(which){
22848 ds.load({params:{start: 0, limit: this.pageSize}});
22851 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22854 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22857 var total = ds.getTotalCount();
22858 var extra = total % this.pageSize;
22859 var lastStart = extra ? (total - extra) : total-this.pageSize;
22860 ds.load({params:{start: lastStart, limit: this.pageSize}});
22863 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22869 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22870 * @param {Roo.data.Store} store The data store to unbind
22872 unbind : function(ds){
22873 ds.un("beforeload", this.beforeLoad, this);
22874 ds.un("load", this.onLoad, this);
22875 ds.un("loadexception", this.onLoadError, this);
22876 ds.un("remove", this.updateInfo, this);
22877 ds.un("add", this.updateInfo, this);
22878 this.ds = undefined;
22882 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22883 * @param {Roo.data.Store} store The data store to bind
22885 bind : function(ds){
22886 ds.on("beforeload", this.beforeLoad, this);
22887 ds.on("load", this.onLoad, this);
22888 ds.on("loadexception", this.onLoadError, this);
22889 ds.on("remove", this.updateInfo, this);
22890 ds.on("add", this.updateInfo, this);
22901 * @class Roo.bootstrap.MessageBar
22902 * @extends Roo.bootstrap.Component
22903 * Bootstrap MessageBar class
22904 * @cfg {String} html contents of the MessageBar
22905 * @cfg {String} weight (info | success | warning | danger) default info
22906 * @cfg {String} beforeClass insert the bar before the given class
22907 * @cfg {Boolean} closable (true | false) default false
22908 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22911 * Create a new Element
22912 * @param {Object} config The config object
22915 Roo.bootstrap.MessageBar = function(config){
22916 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22919 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22925 beforeClass: 'bootstrap-sticky-wrap',
22927 getAutoCreate : function(){
22931 cls: 'alert alert-dismissable alert-' + this.weight,
22936 html: this.html || ''
22942 cfg.cls += ' alert-messages-fixed';
22956 onRender : function(ct, position)
22958 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22961 var cfg = Roo.apply({}, this.getAutoCreate());
22965 cfg.cls += ' ' + this.cls;
22968 cfg.style = this.style;
22970 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22972 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22975 this.el.select('>button.close').on('click', this.hide, this);
22981 if (!this.rendered) {
22987 this.fireEvent('show', this);
22993 if (!this.rendered) {
22999 this.fireEvent('hide', this);
23002 update : function()
23004 // var e = this.el.dom.firstChild;
23006 // if(this.closable){
23007 // e = e.nextSibling;
23010 // e.data = this.html || '';
23012 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23028 * @class Roo.bootstrap.Graph
23029 * @extends Roo.bootstrap.Component
23030 * Bootstrap Graph class
23034 @cfg {String} graphtype bar | vbar | pie
23035 @cfg {number} g_x coodinator | centre x (pie)
23036 @cfg {number} g_y coodinator | centre y (pie)
23037 @cfg {number} g_r radius (pie)
23038 @cfg {number} g_height height of the chart (respected by all elements in the set)
23039 @cfg {number} g_width width of the chart (respected by all elements in the set)
23040 @cfg {Object} title The title of the chart
23043 -opts (object) options for the chart
23045 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23046 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23048 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.
23049 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23051 o stretch (boolean)
23053 -opts (object) options for the pie
23056 o startAngle (number)
23057 o endAngle (number)
23061 * Create a new Input
23062 * @param {Object} config The config object
23065 Roo.bootstrap.Graph = function(config){
23066 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23072 * The img click event for the img.
23073 * @param {Roo.EventObject} e
23079 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23090 //g_colors: this.colors,
23097 getAutoCreate : function(){
23108 onRender : function(ct,position){
23111 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23113 if (typeof(Raphael) == 'undefined') {
23114 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23118 this.raphael = Raphael(this.el.dom);
23120 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23121 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23122 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23123 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23125 r.text(160, 10, "Single Series Chart").attr(txtattr);
23126 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23127 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23128 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23130 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23131 r.barchart(330, 10, 300, 220, data1);
23132 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23133 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23136 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23137 // r.barchart(30, 30, 560, 250, xdata, {
23138 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23139 // axis : "0 0 1 1",
23140 // axisxlabels : xdata
23141 // //yvalues : cols,
23144 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23146 // this.load(null,xdata,{
23147 // axis : "0 0 1 1",
23148 // axisxlabels : xdata
23153 load : function(graphtype,xdata,opts)
23155 this.raphael.clear();
23157 graphtype = this.graphtype;
23162 var r = this.raphael,
23163 fin = function () {
23164 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23166 fout = function () {
23167 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23169 pfin = function() {
23170 this.sector.stop();
23171 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23174 this.label[0].stop();
23175 this.label[0].attr({ r: 7.5 });
23176 this.label[1].attr({ "font-weight": 800 });
23179 pfout = function() {
23180 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23183 this.label[0].animate({ r: 5 }, 500, "bounce");
23184 this.label[1].attr({ "font-weight": 400 });
23190 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23193 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23196 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23197 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23199 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23206 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23211 setTitle: function(o)
23216 initEvents: function() {
23219 this.el.on('click', this.onClick, this);
23223 onClick : function(e)
23225 Roo.log('img onclick');
23226 this.fireEvent('click', this, e);
23238 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23241 * @class Roo.bootstrap.dash.NumberBox
23242 * @extends Roo.bootstrap.Component
23243 * Bootstrap NumberBox class
23244 * @cfg {String} headline Box headline
23245 * @cfg {String} content Box content
23246 * @cfg {String} icon Box icon
23247 * @cfg {String} footer Footer text
23248 * @cfg {String} fhref Footer href
23251 * Create a new NumberBox
23252 * @param {Object} config The config object
23256 Roo.bootstrap.dash.NumberBox = function(config){
23257 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23261 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23270 getAutoCreate : function(){
23274 cls : 'small-box ',
23282 cls : 'roo-headline',
23283 html : this.headline
23287 cls : 'roo-content',
23288 html : this.content
23302 cls : 'ion ' + this.icon
23311 cls : 'small-box-footer',
23312 href : this.fhref || '#',
23316 cfg.cn.push(footer);
23323 onRender : function(ct,position){
23324 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23331 setHeadline: function (value)
23333 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23336 setFooter: function (value, href)
23338 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23341 this.el.select('a.small-box-footer',true).first().attr('href', href);
23346 setContent: function (value)
23348 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23351 initEvents: function()
23365 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23368 * @class Roo.bootstrap.dash.TabBox
23369 * @extends Roo.bootstrap.Component
23370 * Bootstrap TabBox class
23371 * @cfg {String} title Title of the TabBox
23372 * @cfg {String} icon Icon of the TabBox
23373 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23374 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23377 * Create a new TabBox
23378 * @param {Object} config The config object
23382 Roo.bootstrap.dash.TabBox = function(config){
23383 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23388 * When a pane is added
23389 * @param {Roo.bootstrap.dash.TabPane} pane
23393 * @event activatepane
23394 * When a pane is activated
23395 * @param {Roo.bootstrap.dash.TabPane} pane
23397 "activatepane" : true
23405 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23410 tabScrollable : false,
23412 getChildContainer : function()
23414 return this.el.select('.tab-content', true).first();
23417 getAutoCreate : function(){
23421 cls: 'pull-left header',
23429 cls: 'fa ' + this.icon
23435 cls: 'nav nav-tabs pull-right',
23441 if(this.tabScrollable){
23448 cls: 'nav nav-tabs pull-right',
23459 cls: 'nav-tabs-custom',
23464 cls: 'tab-content no-padding',
23472 initEvents : function()
23474 //Roo.log('add add pane handler');
23475 this.on('addpane', this.onAddPane, this);
23478 * Updates the box title
23479 * @param {String} html to set the title to.
23481 setTitle : function(value)
23483 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23485 onAddPane : function(pane)
23487 this.panes.push(pane);
23488 //Roo.log('addpane');
23490 // tabs are rendere left to right..
23491 if(!this.showtabs){
23495 var ctr = this.el.select('.nav-tabs', true).first();
23498 var existing = ctr.select('.nav-tab',true);
23499 var qty = existing.getCount();;
23502 var tab = ctr.createChild({
23504 cls : 'nav-tab' + (qty ? '' : ' active'),
23512 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23515 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23517 pane.el.addClass('active');
23522 onTabClick : function(ev,un,ob,pane)
23524 //Roo.log('tab - prev default');
23525 ev.preventDefault();
23528 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23529 pane.tab.addClass('active');
23530 //Roo.log(pane.title);
23531 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23532 // technically we should have a deactivate event.. but maybe add later.
23533 // and it should not de-activate the selected tab...
23534 this.fireEvent('activatepane', pane);
23535 pane.el.addClass('active');
23536 pane.fireEvent('activate');
23541 getActivePane : function()
23544 Roo.each(this.panes, function(p) {
23545 if(p.el.hasClass('active')){
23566 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23568 * @class Roo.bootstrap.TabPane
23569 * @extends Roo.bootstrap.Component
23570 * Bootstrap TabPane class
23571 * @cfg {Boolean} active (false | true) Default false
23572 * @cfg {String} title title of panel
23576 * Create a new TabPane
23577 * @param {Object} config The config object
23580 Roo.bootstrap.dash.TabPane = function(config){
23581 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23587 * When a pane is activated
23588 * @param {Roo.bootstrap.dash.TabPane} pane
23595 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23600 // the tabBox that this is attached to.
23603 getAutoCreate : function()
23611 cfg.cls += ' active';
23616 initEvents : function()
23618 //Roo.log('trigger add pane handler');
23619 this.parent().fireEvent('addpane', this)
23623 * Updates the tab title
23624 * @param {String} html to set the title to.
23626 setTitle: function(str)
23632 this.tab.select('a', true).first().dom.innerHTML = str;
23649 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23652 * @class Roo.bootstrap.menu.Menu
23653 * @extends Roo.bootstrap.Component
23654 * Bootstrap Menu class - container for Menu
23655 * @cfg {String} html Text of the menu
23656 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23657 * @cfg {String} icon Font awesome icon
23658 * @cfg {String} pos Menu align to (top | bottom) default bottom
23662 * Create a new Menu
23663 * @param {Object} config The config object
23667 Roo.bootstrap.menu.Menu = function(config){
23668 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23672 * @event beforeshow
23673 * Fires before this menu is displayed
23674 * @param {Roo.bootstrap.menu.Menu} this
23678 * @event beforehide
23679 * Fires before this menu is hidden
23680 * @param {Roo.bootstrap.menu.Menu} this
23685 * Fires after this menu is displayed
23686 * @param {Roo.bootstrap.menu.Menu} this
23691 * Fires after this menu is hidden
23692 * @param {Roo.bootstrap.menu.Menu} this
23697 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23698 * @param {Roo.bootstrap.menu.Menu} this
23699 * @param {Roo.EventObject} e
23706 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23710 weight : 'default',
23715 getChildContainer : function() {
23716 if(this.isSubMenu){
23720 return this.el.select('ul.dropdown-menu', true).first();
23723 getAutoCreate : function()
23728 cls : 'roo-menu-text',
23736 cls : 'fa ' + this.icon
23747 cls : 'dropdown-button btn btn-' + this.weight,
23752 cls : 'dropdown-toggle btn btn-' + this.weight,
23762 cls : 'dropdown-menu'
23768 if(this.pos == 'top'){
23769 cfg.cls += ' dropup';
23772 if(this.isSubMenu){
23775 cls : 'dropdown-menu'
23782 onRender : function(ct, position)
23784 this.isSubMenu = ct.hasClass('dropdown-submenu');
23786 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23789 initEvents : function()
23791 if(this.isSubMenu){
23795 this.hidden = true;
23797 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23798 this.triggerEl.on('click', this.onTriggerPress, this);
23800 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23801 this.buttonEl.on('click', this.onClick, this);
23807 if(this.isSubMenu){
23811 return this.el.select('ul.dropdown-menu', true).first();
23814 onClick : function(e)
23816 this.fireEvent("click", this, e);
23819 onTriggerPress : function(e)
23821 if (this.isVisible()) {
23828 isVisible : function(){
23829 return !this.hidden;
23834 this.fireEvent("beforeshow", this);
23836 this.hidden = false;
23837 this.el.addClass('open');
23839 Roo.get(document).on("mouseup", this.onMouseUp, this);
23841 this.fireEvent("show", this);
23848 this.fireEvent("beforehide", this);
23850 this.hidden = true;
23851 this.el.removeClass('open');
23853 Roo.get(document).un("mouseup", this.onMouseUp);
23855 this.fireEvent("hide", this);
23858 onMouseUp : function()
23872 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23875 * @class Roo.bootstrap.menu.Item
23876 * @extends Roo.bootstrap.Component
23877 * Bootstrap MenuItem class
23878 * @cfg {Boolean} submenu (true | false) default false
23879 * @cfg {String} html text of the item
23880 * @cfg {String} href the link
23881 * @cfg {Boolean} disable (true | false) default false
23882 * @cfg {Boolean} preventDefault (true | false) default true
23883 * @cfg {String} icon Font awesome icon
23884 * @cfg {String} pos Submenu align to (left | right) default right
23888 * Create a new Item
23889 * @param {Object} config The config object
23893 Roo.bootstrap.menu.Item = function(config){
23894 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23898 * Fires when the mouse is hovering over this menu
23899 * @param {Roo.bootstrap.menu.Item} this
23900 * @param {Roo.EventObject} e
23905 * Fires when the mouse exits this menu
23906 * @param {Roo.bootstrap.menu.Item} this
23907 * @param {Roo.EventObject} e
23913 * The raw click event for the entire grid.
23914 * @param {Roo.EventObject} e
23920 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23925 preventDefault: true,
23930 getAutoCreate : function()
23935 cls : 'roo-menu-item-text',
23943 cls : 'fa ' + this.icon
23952 href : this.href || '#',
23959 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23963 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23965 if(this.pos == 'left'){
23966 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23973 initEvents : function()
23975 this.el.on('mouseover', this.onMouseOver, this);
23976 this.el.on('mouseout', this.onMouseOut, this);
23978 this.el.select('a', true).first().on('click', this.onClick, this);
23982 onClick : function(e)
23984 if(this.preventDefault){
23985 e.preventDefault();
23988 this.fireEvent("click", this, e);
23991 onMouseOver : function(e)
23993 if(this.submenu && this.pos == 'left'){
23994 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23997 this.fireEvent("mouseover", this, e);
24000 onMouseOut : function(e)
24002 this.fireEvent("mouseout", this, e);
24014 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24017 * @class Roo.bootstrap.menu.Separator
24018 * @extends Roo.bootstrap.Component
24019 * Bootstrap Separator class
24022 * Create a new Separator
24023 * @param {Object} config The config object
24027 Roo.bootstrap.menu.Separator = function(config){
24028 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24031 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24033 getAutoCreate : function(){
24054 * @class Roo.bootstrap.Tooltip
24055 * Bootstrap Tooltip class
24056 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24057 * to determine which dom element triggers the tooltip.
24059 * It needs to add support for additional attributes like tooltip-position
24062 * Create a new Toolti
24063 * @param {Object} config The config object
24066 Roo.bootstrap.Tooltip = function(config){
24067 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24070 Roo.apply(Roo.bootstrap.Tooltip, {
24072 * @function init initialize tooltip monitoring.
24076 currentTip : false,
24077 currentRegion : false,
24083 Roo.get(document).on('mouseover', this.enter ,this);
24084 Roo.get(document).on('mouseout', this.leave, this);
24087 this.currentTip = new Roo.bootstrap.Tooltip();
24090 enter : function(ev)
24092 var dom = ev.getTarget();
24094 //Roo.log(['enter',dom]);
24095 var el = Roo.fly(dom);
24096 if (this.currentEl) {
24098 //Roo.log(this.currentEl);
24099 //Roo.log(this.currentEl.contains(dom));
24100 if (this.currentEl == el) {
24103 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24109 if (this.currentTip.el) {
24110 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24115 // you can not look for children, as if el is the body.. then everythign is the child..
24116 if (!el.attr('tooltip')) { //
24117 if (!el.select("[tooltip]").elements.length) {
24120 // is the mouse over this child...?
24121 bindEl = el.select("[tooltip]").first();
24122 var xy = ev.getXY();
24123 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24124 //Roo.log("not in region.");
24127 //Roo.log("child element over..");
24130 this.currentEl = bindEl;
24131 this.currentTip.bind(bindEl);
24132 this.currentRegion = Roo.lib.Region.getRegion(dom);
24133 this.currentTip.enter();
24136 leave : function(ev)
24138 var dom = ev.getTarget();
24139 //Roo.log(['leave',dom]);
24140 if (!this.currentEl) {
24145 if (dom != this.currentEl.dom) {
24148 var xy = ev.getXY();
24149 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24152 // only activate leave if mouse cursor is outside... bounding box..
24157 if (this.currentTip) {
24158 this.currentTip.leave();
24160 //Roo.log('clear currentEl');
24161 this.currentEl = false;
24166 'left' : ['r-l', [-2,0], 'right'],
24167 'right' : ['l-r', [2,0], 'left'],
24168 'bottom' : ['t-b', [0,2], 'top'],
24169 'top' : [ 'b-t', [0,-2], 'bottom']
24175 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24180 delay : null, // can be { show : 300 , hide: 500}
24184 hoverState : null, //???
24186 placement : 'bottom',
24188 getAutoCreate : function(){
24195 cls : 'tooltip-arrow'
24198 cls : 'tooltip-inner'
24205 bind : function(el)
24211 enter : function () {
24213 if (this.timeout != null) {
24214 clearTimeout(this.timeout);
24217 this.hoverState = 'in';
24218 //Roo.log("enter - show");
24219 if (!this.delay || !this.delay.show) {
24224 this.timeout = setTimeout(function () {
24225 if (_t.hoverState == 'in') {
24228 }, this.delay.show);
24232 clearTimeout(this.timeout);
24234 this.hoverState = 'out';
24235 if (!this.delay || !this.delay.hide) {
24241 this.timeout = setTimeout(function () {
24242 //Roo.log("leave - timeout");
24244 if (_t.hoverState == 'out') {
24246 Roo.bootstrap.Tooltip.currentEl = false;
24254 this.render(document.body);
24257 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24259 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24261 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24263 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24265 var placement = typeof this.placement == 'function' ?
24266 this.placement.call(this, this.el, on_el) :
24269 var autoToken = /\s?auto?\s?/i;
24270 var autoPlace = autoToken.test(placement);
24272 placement = placement.replace(autoToken, '') || 'top';
24276 //this.el.setXY([0,0]);
24278 //this.el.dom.style.display='block';
24280 //this.el.appendTo(on_el);
24282 var p = this.getPosition();
24283 var box = this.el.getBox();
24289 var align = Roo.bootstrap.Tooltip.alignment[placement];
24291 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24293 if(placement == 'top' || placement == 'bottom'){
24295 placement = 'right';
24298 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24299 placement = 'left';
24303 align = Roo.bootstrap.Tooltip.alignment[placement];
24305 this.el.alignTo(this.bindEl, align[0],align[1]);
24306 //var arrow = this.el.select('.arrow',true).first();
24307 //arrow.set(align[2],
24309 this.el.addClass(placement);
24311 this.el.addClass('in fade');
24313 this.hoverState = null;
24315 if (this.el.hasClass('fade')) {
24326 //this.el.setXY([0,0]);
24327 this.el.removeClass('in');
24343 * @class Roo.bootstrap.LocationPicker
24344 * @extends Roo.bootstrap.Component
24345 * Bootstrap LocationPicker class
24346 * @cfg {Number} latitude Position when init default 0
24347 * @cfg {Number} longitude Position when init default 0
24348 * @cfg {Number} zoom default 15
24349 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24350 * @cfg {Boolean} mapTypeControl default false
24351 * @cfg {Boolean} disableDoubleClickZoom default false
24352 * @cfg {Boolean} scrollwheel default true
24353 * @cfg {Boolean} streetViewControl default false
24354 * @cfg {Number} radius default 0
24355 * @cfg {String} locationName
24356 * @cfg {Boolean} draggable default true
24357 * @cfg {Boolean} enableAutocomplete default false
24358 * @cfg {Boolean} enableReverseGeocode default true
24359 * @cfg {String} markerTitle
24362 * Create a new LocationPicker
24363 * @param {Object} config The config object
24367 Roo.bootstrap.LocationPicker = function(config){
24369 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24374 * Fires when the picker initialized.
24375 * @param {Roo.bootstrap.LocationPicker} this
24376 * @param {Google Location} location
24380 * @event positionchanged
24381 * Fires when the picker position changed.
24382 * @param {Roo.bootstrap.LocationPicker} this
24383 * @param {Google Location} location
24385 positionchanged : true,
24388 * Fires when the map resize.
24389 * @param {Roo.bootstrap.LocationPicker} this
24394 * Fires when the map show.
24395 * @param {Roo.bootstrap.LocationPicker} this
24400 * Fires when the map hide.
24401 * @param {Roo.bootstrap.LocationPicker} this
24406 * Fires when click the map.
24407 * @param {Roo.bootstrap.LocationPicker} this
24408 * @param {Map event} e
24412 * @event mapRightClick
24413 * Fires when right click the map.
24414 * @param {Roo.bootstrap.LocationPicker} this
24415 * @param {Map event} e
24417 mapRightClick : true,
24419 * @event markerClick
24420 * Fires when click the marker.
24421 * @param {Roo.bootstrap.LocationPicker} this
24422 * @param {Map event} e
24424 markerClick : true,
24426 * @event markerRightClick
24427 * Fires when right click the marker.
24428 * @param {Roo.bootstrap.LocationPicker} this
24429 * @param {Map event} e
24431 markerRightClick : true,
24433 * @event OverlayViewDraw
24434 * Fires when OverlayView Draw
24435 * @param {Roo.bootstrap.LocationPicker} this
24437 OverlayViewDraw : true,
24439 * @event OverlayViewOnAdd
24440 * Fires when OverlayView Draw
24441 * @param {Roo.bootstrap.LocationPicker} this
24443 OverlayViewOnAdd : true,
24445 * @event OverlayViewOnRemove
24446 * Fires when OverlayView Draw
24447 * @param {Roo.bootstrap.LocationPicker} this
24449 OverlayViewOnRemove : true,
24451 * @event OverlayViewShow
24452 * Fires when OverlayView Draw
24453 * @param {Roo.bootstrap.LocationPicker} this
24454 * @param {Pixel} cpx
24456 OverlayViewShow : true,
24458 * @event OverlayViewHide
24459 * Fires when OverlayView Draw
24460 * @param {Roo.bootstrap.LocationPicker} this
24462 OverlayViewHide : true,
24464 * @event loadexception
24465 * Fires when load google lib failed.
24466 * @param {Roo.bootstrap.LocationPicker} this
24468 loadexception : true
24473 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24475 gMapContext: false,
24481 mapTypeControl: false,
24482 disableDoubleClickZoom: false,
24484 streetViewControl: false,
24488 enableAutocomplete: false,
24489 enableReverseGeocode: true,
24492 getAutoCreate: function()
24497 cls: 'roo-location-picker'
24503 initEvents: function(ct, position)
24505 if(!this.el.getWidth() || this.isApplied()){
24509 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24514 initial: function()
24516 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24517 this.fireEvent('loadexception', this);
24521 if(!this.mapTypeId){
24522 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24525 this.gMapContext = this.GMapContext();
24527 this.initOverlayView();
24529 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24533 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24534 _this.setPosition(_this.gMapContext.marker.position);
24537 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24538 _this.fireEvent('mapClick', this, event);
24542 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24543 _this.fireEvent('mapRightClick', this, event);
24547 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24548 _this.fireEvent('markerClick', this, event);
24552 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24553 _this.fireEvent('markerRightClick', this, event);
24557 this.setPosition(this.gMapContext.location);
24559 this.fireEvent('initial', this, this.gMapContext.location);
24562 initOverlayView: function()
24566 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24570 _this.fireEvent('OverlayViewDraw', _this);
24575 _this.fireEvent('OverlayViewOnAdd', _this);
24578 onRemove: function()
24580 _this.fireEvent('OverlayViewOnRemove', _this);
24583 show: function(cpx)
24585 _this.fireEvent('OverlayViewShow', _this, cpx);
24590 _this.fireEvent('OverlayViewHide', _this);
24596 fromLatLngToContainerPixel: function(event)
24598 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24601 isApplied: function()
24603 return this.getGmapContext() == false ? false : true;
24606 getGmapContext: function()
24608 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24611 GMapContext: function()
24613 var position = new google.maps.LatLng(this.latitude, this.longitude);
24615 var _map = new google.maps.Map(this.el.dom, {
24618 mapTypeId: this.mapTypeId,
24619 mapTypeControl: this.mapTypeControl,
24620 disableDoubleClickZoom: this.disableDoubleClickZoom,
24621 scrollwheel: this.scrollwheel,
24622 streetViewControl: this.streetViewControl,
24623 locationName: this.locationName,
24624 draggable: this.draggable,
24625 enableAutocomplete: this.enableAutocomplete,
24626 enableReverseGeocode: this.enableReverseGeocode
24629 var _marker = new google.maps.Marker({
24630 position: position,
24632 title: this.markerTitle,
24633 draggable: this.draggable
24640 location: position,
24641 radius: this.radius,
24642 locationName: this.locationName,
24643 addressComponents: {
24644 formatted_address: null,
24645 addressLine1: null,
24646 addressLine2: null,
24648 streetNumber: null,
24652 stateOrProvince: null
24655 domContainer: this.el.dom,
24656 geodecoder: new google.maps.Geocoder()
24660 drawCircle: function(center, radius, options)
24662 if (this.gMapContext.circle != null) {
24663 this.gMapContext.circle.setMap(null);
24667 options = Roo.apply({}, options, {
24668 strokeColor: "#0000FF",
24669 strokeOpacity: .35,
24671 fillColor: "#0000FF",
24675 options.map = this.gMapContext.map;
24676 options.radius = radius;
24677 options.center = center;
24678 this.gMapContext.circle = new google.maps.Circle(options);
24679 return this.gMapContext.circle;
24685 setPosition: function(location)
24687 this.gMapContext.location = location;
24688 this.gMapContext.marker.setPosition(location);
24689 this.gMapContext.map.panTo(location);
24690 this.drawCircle(location, this.gMapContext.radius, {});
24694 if (this.gMapContext.settings.enableReverseGeocode) {
24695 this.gMapContext.geodecoder.geocode({
24696 latLng: this.gMapContext.location
24697 }, function(results, status) {
24699 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24700 _this.gMapContext.locationName = results[0].formatted_address;
24701 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24703 _this.fireEvent('positionchanged', this, location);
24710 this.fireEvent('positionchanged', this, location);
24715 google.maps.event.trigger(this.gMapContext.map, "resize");
24717 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24719 this.fireEvent('resize', this);
24722 setPositionByLatLng: function(latitude, longitude)
24724 this.setPosition(new google.maps.LatLng(latitude, longitude));
24727 getCurrentPosition: function()
24730 latitude: this.gMapContext.location.lat(),
24731 longitude: this.gMapContext.location.lng()
24735 getAddressName: function()
24737 return this.gMapContext.locationName;
24740 getAddressComponents: function()
24742 return this.gMapContext.addressComponents;
24745 address_component_from_google_geocode: function(address_components)
24749 for (var i = 0; i < address_components.length; i++) {
24750 var component = address_components[i];
24751 if (component.types.indexOf("postal_code") >= 0) {
24752 result.postalCode = component.short_name;
24753 } else if (component.types.indexOf("street_number") >= 0) {
24754 result.streetNumber = component.short_name;
24755 } else if (component.types.indexOf("route") >= 0) {
24756 result.streetName = component.short_name;
24757 } else if (component.types.indexOf("neighborhood") >= 0) {
24758 result.city = component.short_name;
24759 } else if (component.types.indexOf("locality") >= 0) {
24760 result.city = component.short_name;
24761 } else if (component.types.indexOf("sublocality") >= 0) {
24762 result.district = component.short_name;
24763 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24764 result.stateOrProvince = component.short_name;
24765 } else if (component.types.indexOf("country") >= 0) {
24766 result.country = component.short_name;
24770 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24771 result.addressLine2 = "";
24775 setZoomLevel: function(zoom)
24777 this.gMapContext.map.setZoom(zoom);
24790 this.fireEvent('show', this);
24801 this.fireEvent('hide', this);
24806 Roo.apply(Roo.bootstrap.LocationPicker, {
24808 OverlayView : function(map, options)
24810 options = options || {};
24824 * @class Roo.bootstrap.Alert
24825 * @extends Roo.bootstrap.Component
24826 * Bootstrap Alert class
24827 * @cfg {String} title The title of alert
24828 * @cfg {String} html The content of alert
24829 * @cfg {String} weight ( success | info | warning | danger )
24830 * @cfg {String} faicon font-awesomeicon
24833 * Create a new alert
24834 * @param {Object} config The config object
24838 Roo.bootstrap.Alert = function(config){
24839 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24843 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24850 getAutoCreate : function()
24859 cls : 'roo-alert-icon'
24864 cls : 'roo-alert-title',
24869 cls : 'roo-alert-text',
24876 cfg.cn[0].cls += ' fa ' + this.faicon;
24880 cfg.cls += ' alert-' + this.weight;
24886 initEvents: function()
24888 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24891 setTitle : function(str)
24893 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24896 setText : function(str)
24898 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24901 setWeight : function(weight)
24904 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24907 this.weight = weight;
24909 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24912 setIcon : function(icon)
24915 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24918 this.faicon = icon;
24920 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24941 * @class Roo.bootstrap.UploadCropbox
24942 * @extends Roo.bootstrap.Component
24943 * Bootstrap UploadCropbox class
24944 * @cfg {String} emptyText show when image has been loaded
24945 * @cfg {String} rotateNotify show when image too small to rotate
24946 * @cfg {Number} errorTimeout default 3000
24947 * @cfg {Number} minWidth default 300
24948 * @cfg {Number} minHeight default 300
24949 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24950 * @cfg {Boolean} isDocument (true|false) default false
24951 * @cfg {String} url action url
24952 * @cfg {String} paramName default 'imageUpload'
24953 * @cfg {String} method default POST
24954 * @cfg {Boolean} loadMask (true|false) default true
24955 * @cfg {Boolean} loadingText default 'Loading...'
24958 * Create a new UploadCropbox
24959 * @param {Object} config The config object
24962 Roo.bootstrap.UploadCropbox = function(config){
24963 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24967 * @event beforeselectfile
24968 * Fire before select file
24969 * @param {Roo.bootstrap.UploadCropbox} this
24971 "beforeselectfile" : true,
24974 * Fire after initEvent
24975 * @param {Roo.bootstrap.UploadCropbox} this
24980 * Fire after initEvent
24981 * @param {Roo.bootstrap.UploadCropbox} this
24982 * @param {String} data
24987 * Fire when preparing the file data
24988 * @param {Roo.bootstrap.UploadCropbox} this
24989 * @param {Object} file
24994 * Fire when get exception
24995 * @param {Roo.bootstrap.UploadCropbox} this
24996 * @param {XMLHttpRequest} xhr
24998 "exception" : true,
25000 * @event beforeloadcanvas
25001 * Fire before load the canvas
25002 * @param {Roo.bootstrap.UploadCropbox} this
25003 * @param {String} src
25005 "beforeloadcanvas" : true,
25008 * Fire when trash image
25009 * @param {Roo.bootstrap.UploadCropbox} this
25014 * Fire when download the image
25015 * @param {Roo.bootstrap.UploadCropbox} this
25019 * @event footerbuttonclick
25020 * Fire when footerbuttonclick
25021 * @param {Roo.bootstrap.UploadCropbox} this
25022 * @param {String} type
25024 "footerbuttonclick" : true,
25028 * @param {Roo.bootstrap.UploadCropbox} this
25033 * Fire when rotate the image
25034 * @param {Roo.bootstrap.UploadCropbox} this
25035 * @param {String} pos
25040 * Fire when inspect the file
25041 * @param {Roo.bootstrap.UploadCropbox} this
25042 * @param {Object} file
25047 * Fire when xhr upload the file
25048 * @param {Roo.bootstrap.UploadCropbox} this
25049 * @param {Object} data
25054 * Fire when arrange the file data
25055 * @param {Roo.bootstrap.UploadCropbox} this
25056 * @param {Object} formData
25061 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25064 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25066 emptyText : 'Click to upload image',
25067 rotateNotify : 'Image is too small to rotate',
25068 errorTimeout : 3000,
25082 cropType : 'image/jpeg',
25084 canvasLoaded : false,
25085 isDocument : false,
25087 paramName : 'imageUpload',
25089 loadingText : 'Loading...',
25092 getAutoCreate : function()
25096 cls : 'roo-upload-cropbox',
25100 cls : 'roo-upload-cropbox-selector',
25105 cls : 'roo-upload-cropbox-body',
25106 style : 'cursor:pointer',
25110 cls : 'roo-upload-cropbox-preview'
25114 cls : 'roo-upload-cropbox-thumb'
25118 cls : 'roo-upload-cropbox-empty-notify',
25119 html : this.emptyText
25123 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25124 html : this.rotateNotify
25130 cls : 'roo-upload-cropbox-footer',
25133 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25143 onRender : function(ct, position)
25145 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25147 if (this.buttons.length) {
25149 Roo.each(this.buttons, function(bb) {
25151 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25153 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25159 this.maskEl = this.el;
25163 initEvents : function()
25165 this.urlAPI = (window.createObjectURL && window) ||
25166 (window.URL && URL.revokeObjectURL && URL) ||
25167 (window.webkitURL && webkitURL);
25169 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25170 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25172 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25173 this.selectorEl.hide();
25175 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25176 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25178 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25179 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25180 this.thumbEl.hide();
25182 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25183 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25185 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25186 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25187 this.errorEl.hide();
25189 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25190 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25191 this.footerEl.hide();
25193 this.setThumbBoxSize();
25199 this.fireEvent('initial', this);
25206 window.addEventListener("resize", function() { _this.resize(); } );
25208 this.bodyEl.on('click', this.beforeSelectFile, this);
25211 this.bodyEl.on('touchstart', this.onTouchStart, this);
25212 this.bodyEl.on('touchmove', this.onTouchMove, this);
25213 this.bodyEl.on('touchend', this.onTouchEnd, this);
25217 this.bodyEl.on('mousedown', this.onMouseDown, this);
25218 this.bodyEl.on('mousemove', this.onMouseMove, this);
25219 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25220 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25221 Roo.get(document).on('mouseup', this.onMouseUp, this);
25224 this.selectorEl.on('change', this.onFileSelected, this);
25230 this.baseScale = 1;
25232 this.baseRotate = 1;
25233 this.dragable = false;
25234 this.pinching = false;
25237 this.cropData = false;
25238 this.notifyEl.dom.innerHTML = this.emptyText;
25240 this.selectorEl.dom.value = '';
25244 resize : function()
25246 if(this.fireEvent('resize', this) != false){
25247 this.setThumbBoxPosition();
25248 this.setCanvasPosition();
25252 onFooterButtonClick : function(e, el, o, type)
25255 case 'rotate-left' :
25256 this.onRotateLeft(e);
25258 case 'rotate-right' :
25259 this.onRotateRight(e);
25262 this.beforeSelectFile(e);
25277 this.fireEvent('footerbuttonclick', this, type);
25280 beforeSelectFile : function(e)
25282 e.preventDefault();
25284 if(this.fireEvent('beforeselectfile', this) != false){
25285 this.selectorEl.dom.click();
25289 onFileSelected : function(e)
25291 e.preventDefault();
25293 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25297 var file = this.selectorEl.dom.files[0];
25299 if(this.fireEvent('inspect', this, file) != false){
25300 this.prepare(file);
25305 trash : function(e)
25307 this.fireEvent('trash', this);
25310 download : function(e)
25312 this.fireEvent('download', this);
25315 loadCanvas : function(src)
25317 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25321 this.imageEl = document.createElement('img');
25325 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25327 this.imageEl.src = src;
25331 onLoadCanvas : function()
25333 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25334 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25336 this.bodyEl.un('click', this.beforeSelectFile, this);
25338 this.notifyEl.hide();
25339 this.thumbEl.show();
25340 this.footerEl.show();
25342 this.baseRotateLevel();
25344 if(this.isDocument){
25345 this.setThumbBoxSize();
25348 this.setThumbBoxPosition();
25350 this.baseScaleLevel();
25356 this.canvasLoaded = true;
25359 this.maskEl.unmask();
25364 setCanvasPosition : function()
25366 if(!this.canvasEl){
25370 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25371 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25373 this.previewEl.setLeft(pw);
25374 this.previewEl.setTop(ph);
25378 onMouseDown : function(e)
25382 this.dragable = true;
25383 this.pinching = false;
25385 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25386 this.dragable = false;
25390 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25391 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25395 onMouseMove : function(e)
25399 if(!this.canvasLoaded){
25403 if (!this.dragable){
25407 var minX = Math.ceil(this.thumbEl.getLeft(true));
25408 var minY = Math.ceil(this.thumbEl.getTop(true));
25410 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25411 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25413 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25414 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25416 x = x - this.mouseX;
25417 y = y - this.mouseY;
25419 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25420 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25422 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25423 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25425 this.previewEl.setLeft(bgX);
25426 this.previewEl.setTop(bgY);
25428 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25429 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25432 onMouseUp : function(e)
25436 this.dragable = false;
25439 onMouseWheel : function(e)
25443 this.startScale = this.scale;
25445 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25447 if(!this.zoomable()){
25448 this.scale = this.startScale;
25457 zoomable : function()
25459 var minScale = this.thumbEl.getWidth() / this.minWidth;
25461 if(this.minWidth < this.minHeight){
25462 minScale = this.thumbEl.getHeight() / this.minHeight;
25465 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25466 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25470 (this.rotate == 0 || this.rotate == 180) &&
25472 width > this.imageEl.OriginWidth ||
25473 height > this.imageEl.OriginHeight ||
25474 (width < this.minWidth && height < this.minHeight)
25482 (this.rotate == 90 || this.rotate == 270) &&
25484 width > this.imageEl.OriginWidth ||
25485 height > this.imageEl.OriginHeight ||
25486 (width < this.minHeight && height < this.minWidth)
25493 !this.isDocument &&
25494 (this.rotate == 0 || this.rotate == 180) &&
25496 width < this.minWidth ||
25497 width > this.imageEl.OriginWidth ||
25498 height < this.minHeight ||
25499 height > this.imageEl.OriginHeight
25506 !this.isDocument &&
25507 (this.rotate == 90 || this.rotate == 270) &&
25509 width < this.minHeight ||
25510 width > this.imageEl.OriginWidth ||
25511 height < this.minWidth ||
25512 height > this.imageEl.OriginHeight
25522 onRotateLeft : function(e)
25524 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25526 var minScale = this.thumbEl.getWidth() / this.minWidth;
25528 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25529 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25531 this.startScale = this.scale;
25533 while (this.getScaleLevel() < minScale){
25535 this.scale = this.scale + 1;
25537 if(!this.zoomable()){
25542 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25543 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25548 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25555 this.scale = this.startScale;
25557 this.onRotateFail();
25562 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25564 if(this.isDocument){
25565 this.setThumbBoxSize();
25566 this.setThumbBoxPosition();
25567 this.setCanvasPosition();
25572 this.fireEvent('rotate', this, 'left');
25576 onRotateRight : function(e)
25578 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25580 var minScale = this.thumbEl.getWidth() / this.minWidth;
25582 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25583 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25585 this.startScale = this.scale;
25587 while (this.getScaleLevel() < minScale){
25589 this.scale = this.scale + 1;
25591 if(!this.zoomable()){
25596 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25597 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25602 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25609 this.scale = this.startScale;
25611 this.onRotateFail();
25616 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25618 if(this.isDocument){
25619 this.setThumbBoxSize();
25620 this.setThumbBoxPosition();
25621 this.setCanvasPosition();
25626 this.fireEvent('rotate', this, 'right');
25629 onRotateFail : function()
25631 this.errorEl.show(true);
25635 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25640 this.previewEl.dom.innerHTML = '';
25642 var canvasEl = document.createElement("canvas");
25644 var contextEl = canvasEl.getContext("2d");
25646 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25647 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25648 var center = this.imageEl.OriginWidth / 2;
25650 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25651 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25652 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25653 center = this.imageEl.OriginHeight / 2;
25656 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25658 contextEl.translate(center, center);
25659 contextEl.rotate(this.rotate * Math.PI / 180);
25661 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25663 this.canvasEl = document.createElement("canvas");
25665 this.contextEl = this.canvasEl.getContext("2d");
25667 switch (this.rotate) {
25670 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25671 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25673 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25678 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25679 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25681 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25682 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);
25686 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25691 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25692 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25694 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25695 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);
25699 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);
25704 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25705 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25707 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25708 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25712 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);
25719 this.previewEl.appendChild(this.canvasEl);
25721 this.setCanvasPosition();
25726 if(!this.canvasLoaded){
25730 var imageCanvas = document.createElement("canvas");
25732 var imageContext = imageCanvas.getContext("2d");
25734 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25735 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25737 var center = imageCanvas.width / 2;
25739 imageContext.translate(center, center);
25741 imageContext.rotate(this.rotate * Math.PI / 180);
25743 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25745 var canvas = document.createElement("canvas");
25747 var context = canvas.getContext("2d");
25749 canvas.width = this.minWidth;
25750 canvas.height = this.minHeight;
25752 switch (this.rotate) {
25755 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25756 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25758 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25759 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25761 var targetWidth = this.minWidth - 2 * x;
25762 var targetHeight = this.minHeight - 2 * y;
25766 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25767 scale = targetWidth / width;
25770 if(x > 0 && y == 0){
25771 scale = targetHeight / height;
25774 if(x > 0 && y > 0){
25775 scale = targetWidth / width;
25777 if(width < height){
25778 scale = targetHeight / height;
25782 context.scale(scale, scale);
25784 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25785 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25787 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25788 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25790 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25795 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25796 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25798 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25799 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25801 var targetWidth = this.minWidth - 2 * x;
25802 var targetHeight = this.minHeight - 2 * y;
25806 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25807 scale = targetWidth / width;
25810 if(x > 0 && y == 0){
25811 scale = targetHeight / height;
25814 if(x > 0 && y > 0){
25815 scale = targetWidth / width;
25817 if(width < height){
25818 scale = targetHeight / height;
25822 context.scale(scale, scale);
25824 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25825 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25827 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25828 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25830 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25832 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25837 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25838 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25840 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25841 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25843 var targetWidth = this.minWidth - 2 * x;
25844 var targetHeight = this.minHeight - 2 * y;
25848 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25849 scale = targetWidth / width;
25852 if(x > 0 && y == 0){
25853 scale = targetHeight / height;
25856 if(x > 0 && y > 0){
25857 scale = targetWidth / width;
25859 if(width < height){
25860 scale = targetHeight / height;
25864 context.scale(scale, scale);
25866 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25867 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25869 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25870 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25872 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25873 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25875 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25880 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25881 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25883 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25884 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25886 var targetWidth = this.minWidth - 2 * x;
25887 var targetHeight = this.minHeight - 2 * y;
25891 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25892 scale = targetWidth / width;
25895 if(x > 0 && y == 0){
25896 scale = targetHeight / height;
25899 if(x > 0 && y > 0){
25900 scale = targetWidth / width;
25902 if(width < height){
25903 scale = targetHeight / height;
25907 context.scale(scale, scale);
25909 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25910 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25912 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25913 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25915 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25917 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25924 this.cropData = canvas.toDataURL(this.cropType);
25926 if(this.fireEvent('crop', this, this.cropData) !== false){
25927 this.process(this.file, this.cropData);
25934 setThumbBoxSize : function()
25938 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25939 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25940 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25942 this.minWidth = width;
25943 this.minHeight = height;
25945 if(this.rotate == 90 || this.rotate == 270){
25946 this.minWidth = height;
25947 this.minHeight = width;
25952 width = Math.ceil(this.minWidth * height / this.minHeight);
25954 if(this.minWidth > this.minHeight){
25956 height = Math.ceil(this.minHeight * width / this.minWidth);
25959 this.thumbEl.setStyle({
25960 width : width + 'px',
25961 height : height + 'px'
25968 setThumbBoxPosition : function()
25970 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25971 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25973 this.thumbEl.setLeft(x);
25974 this.thumbEl.setTop(y);
25978 baseRotateLevel : function()
25980 this.baseRotate = 1;
25983 typeof(this.exif) != 'undefined' &&
25984 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25985 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25987 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25990 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25994 baseScaleLevel : function()
25998 if(this.isDocument){
26000 if(this.baseRotate == 6 || this.baseRotate == 8){
26002 height = this.thumbEl.getHeight();
26003 this.baseScale = height / this.imageEl.OriginWidth;
26005 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26006 width = this.thumbEl.getWidth();
26007 this.baseScale = width / this.imageEl.OriginHeight;
26013 height = this.thumbEl.getHeight();
26014 this.baseScale = height / this.imageEl.OriginHeight;
26016 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26017 width = this.thumbEl.getWidth();
26018 this.baseScale = width / this.imageEl.OriginWidth;
26024 if(this.baseRotate == 6 || this.baseRotate == 8){
26026 width = this.thumbEl.getHeight();
26027 this.baseScale = width / this.imageEl.OriginHeight;
26029 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26030 height = this.thumbEl.getWidth();
26031 this.baseScale = height / this.imageEl.OriginHeight;
26034 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26035 height = this.thumbEl.getWidth();
26036 this.baseScale = height / this.imageEl.OriginHeight;
26038 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26039 width = this.thumbEl.getHeight();
26040 this.baseScale = width / this.imageEl.OriginWidth;
26047 width = this.thumbEl.getWidth();
26048 this.baseScale = width / this.imageEl.OriginWidth;
26050 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26051 height = this.thumbEl.getHeight();
26052 this.baseScale = height / this.imageEl.OriginHeight;
26055 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26057 height = this.thumbEl.getHeight();
26058 this.baseScale = height / this.imageEl.OriginHeight;
26060 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26061 width = this.thumbEl.getWidth();
26062 this.baseScale = width / this.imageEl.OriginWidth;
26070 getScaleLevel : function()
26072 return this.baseScale * Math.pow(1.1, this.scale);
26075 onTouchStart : function(e)
26077 if(!this.canvasLoaded){
26078 this.beforeSelectFile(e);
26082 var touches = e.browserEvent.touches;
26088 if(touches.length == 1){
26089 this.onMouseDown(e);
26093 if(touches.length != 2){
26099 for(var i = 0, finger; finger = touches[i]; i++){
26100 coords.push(finger.pageX, finger.pageY);
26103 var x = Math.pow(coords[0] - coords[2], 2);
26104 var y = Math.pow(coords[1] - coords[3], 2);
26106 this.startDistance = Math.sqrt(x + y);
26108 this.startScale = this.scale;
26110 this.pinching = true;
26111 this.dragable = false;
26115 onTouchMove : function(e)
26117 if(!this.pinching && !this.dragable){
26121 var touches = e.browserEvent.touches;
26128 this.onMouseMove(e);
26134 for(var i = 0, finger; finger = touches[i]; i++){
26135 coords.push(finger.pageX, finger.pageY);
26138 var x = Math.pow(coords[0] - coords[2], 2);
26139 var y = Math.pow(coords[1] - coords[3], 2);
26141 this.endDistance = Math.sqrt(x + y);
26143 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26145 if(!this.zoomable()){
26146 this.scale = this.startScale;
26154 onTouchEnd : function(e)
26156 this.pinching = false;
26157 this.dragable = false;
26161 process : function(file, crop)
26164 this.maskEl.mask(this.loadingText);
26167 this.xhr = new XMLHttpRequest();
26169 file.xhr = this.xhr;
26171 this.xhr.open(this.method, this.url, true);
26174 "Accept": "application/json",
26175 "Cache-Control": "no-cache",
26176 "X-Requested-With": "XMLHttpRequest"
26179 for (var headerName in headers) {
26180 var headerValue = headers[headerName];
26182 this.xhr.setRequestHeader(headerName, headerValue);
26188 this.xhr.onload = function()
26190 _this.xhrOnLoad(_this.xhr);
26193 this.xhr.onerror = function()
26195 _this.xhrOnError(_this.xhr);
26198 var formData = new FormData();
26200 formData.append('returnHTML', 'NO');
26203 formData.append('crop', crop);
26206 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26207 formData.append(this.paramName, file, file.name);
26210 if(typeof(file.filename) != 'undefined'){
26211 formData.append('filename', file.filename);
26214 if(typeof(file.mimetype) != 'undefined'){
26215 formData.append('mimetype', file.mimetype);
26218 if(this.fireEvent('arrange', this, formData) != false){
26219 this.xhr.send(formData);
26223 xhrOnLoad : function(xhr)
26226 this.maskEl.unmask();
26229 if (xhr.readyState !== 4) {
26230 this.fireEvent('exception', this, xhr);
26234 var response = Roo.decode(xhr.responseText);
26236 if(!response.success){
26237 this.fireEvent('exception', this, xhr);
26241 var response = Roo.decode(xhr.responseText);
26243 this.fireEvent('upload', this, response);
26247 xhrOnError : function()
26250 this.maskEl.unmask();
26253 Roo.log('xhr on error');
26255 var response = Roo.decode(xhr.responseText);
26261 prepare : function(file)
26264 this.maskEl.mask(this.loadingText);
26270 if(typeof(file) === 'string'){
26271 this.loadCanvas(file);
26275 if(!file || !this.urlAPI){
26280 this.cropType = file.type;
26284 if(this.fireEvent('prepare', this, this.file) != false){
26286 var reader = new FileReader();
26288 reader.onload = function (e) {
26289 if (e.target.error) {
26290 Roo.log(e.target.error);
26294 var buffer = e.target.result,
26295 dataView = new DataView(buffer),
26297 maxOffset = dataView.byteLength - 4,
26301 if (dataView.getUint16(0) === 0xffd8) {
26302 while (offset < maxOffset) {
26303 markerBytes = dataView.getUint16(offset);
26305 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26306 markerLength = dataView.getUint16(offset + 2) + 2;
26307 if (offset + markerLength > dataView.byteLength) {
26308 Roo.log('Invalid meta data: Invalid segment size.');
26312 if(markerBytes == 0xffe1){
26313 _this.parseExifData(
26320 offset += markerLength;
26330 var url = _this.urlAPI.createObjectURL(_this.file);
26332 _this.loadCanvas(url);
26337 reader.readAsArrayBuffer(this.file);
26343 parseExifData : function(dataView, offset, length)
26345 var tiffOffset = offset + 10,
26349 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26350 // No Exif data, might be XMP data instead
26354 // Check for the ASCII code for "Exif" (0x45786966):
26355 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26356 // No Exif data, might be XMP data instead
26359 if (tiffOffset + 8 > dataView.byteLength) {
26360 Roo.log('Invalid Exif data: Invalid segment size.');
26363 // Check for the two null bytes:
26364 if (dataView.getUint16(offset + 8) !== 0x0000) {
26365 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26368 // Check the byte alignment:
26369 switch (dataView.getUint16(tiffOffset)) {
26371 littleEndian = true;
26374 littleEndian = false;
26377 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26380 // Check for the TIFF tag marker (0x002A):
26381 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26382 Roo.log('Invalid Exif data: Missing TIFF marker.');
26385 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26386 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26388 this.parseExifTags(
26391 tiffOffset + dirOffset,
26396 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26401 if (dirOffset + 6 > dataView.byteLength) {
26402 Roo.log('Invalid Exif data: Invalid directory offset.');
26405 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26406 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26407 if (dirEndOffset + 4 > dataView.byteLength) {
26408 Roo.log('Invalid Exif data: Invalid directory size.');
26411 for (i = 0; i < tagsNumber; i += 1) {
26415 dirOffset + 2 + 12 * i, // tag offset
26419 // Return the offset to the next directory:
26420 return dataView.getUint32(dirEndOffset, littleEndian);
26423 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26425 var tag = dataView.getUint16(offset, littleEndian);
26427 this.exif[tag] = this.getExifValue(
26431 dataView.getUint16(offset + 2, littleEndian), // tag type
26432 dataView.getUint32(offset + 4, littleEndian), // tag length
26437 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26439 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26448 Roo.log('Invalid Exif data: Invalid tag type.');
26452 tagSize = tagType.size * length;
26453 // Determine if the value is contained in the dataOffset bytes,
26454 // or if the value at the dataOffset is a pointer to the actual data:
26455 dataOffset = tagSize > 4 ?
26456 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26457 if (dataOffset + tagSize > dataView.byteLength) {
26458 Roo.log('Invalid Exif data: Invalid data offset.');
26461 if (length === 1) {
26462 return tagType.getValue(dataView, dataOffset, littleEndian);
26465 for (i = 0; i < length; i += 1) {
26466 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26469 if (tagType.ascii) {
26471 // Concatenate the chars:
26472 for (i = 0; i < values.length; i += 1) {
26474 // Ignore the terminating NULL byte(s):
26475 if (c === '\u0000') {
26487 Roo.apply(Roo.bootstrap.UploadCropbox, {
26489 'Orientation': 0x0112
26493 1: 0, //'top-left',
26495 3: 180, //'bottom-right',
26496 // 4: 'bottom-left',
26498 6: 90, //'right-top',
26499 // 7: 'right-bottom',
26500 8: 270 //'left-bottom'
26504 // byte, 8-bit unsigned int:
26506 getValue: function (dataView, dataOffset) {
26507 return dataView.getUint8(dataOffset);
26511 // ascii, 8-bit byte:
26513 getValue: function (dataView, dataOffset) {
26514 return String.fromCharCode(dataView.getUint8(dataOffset));
26519 // short, 16 bit int:
26521 getValue: function (dataView, dataOffset, littleEndian) {
26522 return dataView.getUint16(dataOffset, littleEndian);
26526 // long, 32 bit int:
26528 getValue: function (dataView, dataOffset, littleEndian) {
26529 return dataView.getUint32(dataOffset, littleEndian);
26533 // rational = two long values, first is numerator, second is denominator:
26535 getValue: function (dataView, dataOffset, littleEndian) {
26536 return dataView.getUint32(dataOffset, littleEndian) /
26537 dataView.getUint32(dataOffset + 4, littleEndian);
26541 // slong, 32 bit signed int:
26543 getValue: function (dataView, dataOffset, littleEndian) {
26544 return dataView.getInt32(dataOffset, littleEndian);
26548 // srational, two slongs, first is numerator, second is denominator:
26550 getValue: function (dataView, dataOffset, littleEndian) {
26551 return dataView.getInt32(dataOffset, littleEndian) /
26552 dataView.getInt32(dataOffset + 4, littleEndian);
26562 cls : 'btn-group roo-upload-cropbox-rotate-left',
26563 action : 'rotate-left',
26567 cls : 'btn btn-default',
26568 html : '<i class="fa fa-undo"></i>'
26574 cls : 'btn-group roo-upload-cropbox-picture',
26575 action : 'picture',
26579 cls : 'btn btn-default',
26580 html : '<i class="fa fa-picture-o"></i>'
26586 cls : 'btn-group roo-upload-cropbox-rotate-right',
26587 action : 'rotate-right',
26591 cls : 'btn btn-default',
26592 html : '<i class="fa fa-repeat"></i>'
26600 cls : 'btn-group roo-upload-cropbox-rotate-left',
26601 action : 'rotate-left',
26605 cls : 'btn btn-default',
26606 html : '<i class="fa fa-undo"></i>'
26612 cls : 'btn-group roo-upload-cropbox-download',
26613 action : 'download',
26617 cls : 'btn btn-default',
26618 html : '<i class="fa fa-download"></i>'
26624 cls : 'btn-group roo-upload-cropbox-crop',
26629 cls : 'btn btn-default',
26630 html : '<i class="fa fa-crop"></i>'
26636 cls : 'btn-group roo-upload-cropbox-trash',
26641 cls : 'btn btn-default',
26642 html : '<i class="fa fa-trash"></i>'
26648 cls : 'btn-group roo-upload-cropbox-rotate-right',
26649 action : 'rotate-right',
26653 cls : 'btn btn-default',
26654 html : '<i class="fa fa-repeat"></i>'
26662 cls : 'btn-group roo-upload-cropbox-rotate-left',
26663 action : 'rotate-left',
26667 cls : 'btn btn-default',
26668 html : '<i class="fa fa-undo"></i>'
26674 cls : 'btn-group roo-upload-cropbox-rotate-right',
26675 action : 'rotate-right',
26679 cls : 'btn btn-default',
26680 html : '<i class="fa fa-repeat"></i>'
26693 * @class Roo.bootstrap.DocumentManager
26694 * @extends Roo.bootstrap.Component
26695 * Bootstrap DocumentManager class
26696 * @cfg {String} paramName default 'imageUpload'
26697 * @cfg {String} method default POST
26698 * @cfg {String} url action url
26699 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26700 * @cfg {Boolean} multiple multiple upload default true
26701 * @cfg {Number} thumbSize default 300
26702 * @cfg {String} fieldLabel
26703 * @cfg {Number} labelWidth default 4
26704 * @cfg {String} labelAlign (left|top) default left
26705 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26708 * Create a new DocumentManager
26709 * @param {Object} config The config object
26712 Roo.bootstrap.DocumentManager = function(config){
26713 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26718 * Fire when initial the DocumentManager
26719 * @param {Roo.bootstrap.DocumentManager} this
26724 * inspect selected file
26725 * @param {Roo.bootstrap.DocumentManager} this
26726 * @param {File} file
26731 * Fire when xhr load exception
26732 * @param {Roo.bootstrap.DocumentManager} this
26733 * @param {XMLHttpRequest} xhr
26735 "exception" : true,
26738 * prepare the form data
26739 * @param {Roo.bootstrap.DocumentManager} this
26740 * @param {Object} formData
26745 * Fire when remove the file
26746 * @param {Roo.bootstrap.DocumentManager} this
26747 * @param {Object} file
26752 * Fire after refresh the file
26753 * @param {Roo.bootstrap.DocumentManager} this
26758 * Fire after click the image
26759 * @param {Roo.bootstrap.DocumentManager} this
26760 * @param {Object} file
26765 * Fire when upload a image and editable set to true
26766 * @param {Roo.bootstrap.DocumentManager} this
26767 * @param {Object} file
26771 * @event beforeselectfile
26772 * Fire before select file
26773 * @param {Roo.bootstrap.DocumentManager} this
26775 "beforeselectfile" : true,
26778 * Fire before process file
26779 * @param {Roo.bootstrap.DocumentManager} this
26780 * @param {Object} file
26787 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26796 paramName : 'imageUpload',
26799 labelAlign : 'left',
26806 getAutoCreate : function()
26808 var managerWidget = {
26810 cls : 'roo-document-manager',
26814 cls : 'roo-document-manager-selector',
26819 cls : 'roo-document-manager-uploader',
26823 cls : 'roo-document-manager-upload-btn',
26824 html : '<i class="fa fa-plus"></i>'
26835 cls : 'column col-md-12',
26840 if(this.fieldLabel.length){
26845 cls : 'column col-md-12',
26846 html : this.fieldLabel
26850 cls : 'column col-md-12',
26855 if(this.labelAlign == 'left'){
26859 cls : 'column col-md-' + this.labelWidth,
26860 html : this.fieldLabel
26864 cls : 'column col-md-' + (12 - this.labelWidth),
26874 cls : 'row clearfix',
26882 initEvents : function()
26884 this.managerEl = this.el.select('.roo-document-manager', true).first();
26885 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26887 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26888 this.selectorEl.hide();
26891 this.selectorEl.attr('multiple', 'multiple');
26894 this.selectorEl.on('change', this.onFileSelected, this);
26896 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26897 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26899 this.uploader.on('click', this.onUploaderClick, this);
26901 this.renderProgressDialog();
26905 window.addEventListener("resize", function() { _this.refresh(); } );
26907 this.fireEvent('initial', this);
26910 renderProgressDialog : function()
26914 this.progressDialog = new Roo.bootstrap.Modal({
26915 cls : 'roo-document-manager-progress-dialog',
26916 allow_close : false,
26926 btnclick : function() {
26927 _this.uploadCancel();
26933 this.progressDialog.render(Roo.get(document.body));
26935 this.progress = new Roo.bootstrap.Progress({
26936 cls : 'roo-document-manager-progress',
26941 this.progress.render(this.progressDialog.getChildContainer());
26943 this.progressBar = new Roo.bootstrap.ProgressBar({
26944 cls : 'roo-document-manager-progress-bar',
26947 aria_valuemax : 12,
26951 this.progressBar.render(this.progress.getChildContainer());
26954 onUploaderClick : function(e)
26956 e.preventDefault();
26958 if(this.fireEvent('beforeselectfile', this) != false){
26959 this.selectorEl.dom.click();
26964 onFileSelected : function(e)
26966 e.preventDefault();
26968 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26972 Roo.each(this.selectorEl.dom.files, function(file){
26973 if(this.fireEvent('inspect', this, file) != false){
26974 this.files.push(file);
26984 this.selectorEl.dom.value = '';
26986 if(!this.files.length){
26990 if(this.boxes > 0 && this.files.length > this.boxes){
26991 this.files = this.files.slice(0, this.boxes);
26994 this.uploader.show();
26996 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26997 this.uploader.hide();
27006 Roo.each(this.files, function(file){
27008 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27009 var f = this.renderPreview(file);
27014 if(file.type.indexOf('image') != -1){
27015 this.delegates.push(
27017 _this.process(file);
27018 }).createDelegate(this)
27026 _this.process(file);
27027 }).createDelegate(this)
27032 this.files = files;
27034 this.delegates = this.delegates.concat(docs);
27036 if(!this.delegates.length){
27041 this.progressBar.aria_valuemax = this.delegates.length;
27048 arrange : function()
27050 if(!this.delegates.length){
27051 this.progressDialog.hide();
27056 var delegate = this.delegates.shift();
27058 this.progressDialog.show();
27060 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27062 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27067 refresh : function()
27069 this.uploader.show();
27071 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27072 this.uploader.hide();
27075 Roo.isTouch ? this.closable(false) : this.closable(true);
27077 this.fireEvent('refresh', this);
27080 onRemove : function(e, el, o)
27082 e.preventDefault();
27084 this.fireEvent('remove', this, o);
27088 remove : function(o)
27092 Roo.each(this.files, function(file){
27093 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27102 this.files = files;
27109 Roo.each(this.files, function(file){
27114 file.target.remove();
27123 onClick : function(e, el, o)
27125 e.preventDefault();
27127 this.fireEvent('click', this, o);
27131 closable : function(closable)
27133 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27135 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27147 xhrOnLoad : function(xhr)
27149 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27153 if (xhr.readyState !== 4) {
27155 this.fireEvent('exception', this, xhr);
27159 var response = Roo.decode(xhr.responseText);
27161 if(!response.success){
27163 this.fireEvent('exception', this, xhr);
27167 var file = this.renderPreview(response.data);
27169 this.files.push(file);
27175 xhrOnError : function(xhr)
27177 Roo.log('xhr on error');
27179 var response = Roo.decode(xhr.responseText);
27186 process : function(file)
27188 if(this.fireEvent('process', this, file) !== false){
27189 if(this.editable && file.type.indexOf('image') != -1){
27190 this.fireEvent('edit', this, file);
27194 this.uploadStart(file, false);
27201 uploadStart : function(file, crop)
27203 this.xhr = new XMLHttpRequest();
27205 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27210 file.xhr = this.xhr;
27212 this.managerEl.createChild({
27214 cls : 'roo-document-manager-loading',
27218 tooltip : file.name,
27219 cls : 'roo-document-manager-thumb',
27220 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27226 this.xhr.open(this.method, this.url, true);
27229 "Accept": "application/json",
27230 "Cache-Control": "no-cache",
27231 "X-Requested-With": "XMLHttpRequest"
27234 for (var headerName in headers) {
27235 var headerValue = headers[headerName];
27237 this.xhr.setRequestHeader(headerName, headerValue);
27243 this.xhr.onload = function()
27245 _this.xhrOnLoad(_this.xhr);
27248 this.xhr.onerror = function()
27250 _this.xhrOnError(_this.xhr);
27253 var formData = new FormData();
27255 formData.append('returnHTML', 'NO');
27258 formData.append('crop', crop);
27261 formData.append(this.paramName, file, file.name);
27263 if(this.fireEvent('prepare', this, formData) != false){
27264 this.xhr.send(formData);
27268 uploadCancel : function()
27275 this.delegates = [];
27277 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27284 renderPreview : function(file)
27286 if(typeof(file.target) != 'undefined' && file.target){
27290 var previewEl = this.managerEl.createChild({
27292 cls : 'roo-document-manager-preview',
27296 tooltip : file.filename,
27297 cls : 'roo-document-manager-thumb',
27298 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27303 html : '<i class="fa fa-times-circle"></i>'
27308 var close = previewEl.select('button.close', true).first();
27310 close.on('click', this.onRemove, this, file);
27312 file.target = previewEl;
27314 var image = previewEl.select('img', true).first();
27318 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27320 image.on('click', this.onClick, this, file);
27326 onPreviewLoad : function(file, image)
27328 if(typeof(file.target) == 'undefined' || !file.target){
27332 var width = image.dom.naturalWidth || image.dom.width;
27333 var height = image.dom.naturalHeight || image.dom.height;
27335 if(width > height){
27336 file.target.addClass('wide');
27340 file.target.addClass('tall');
27345 uploadFromSource : function(file, crop)
27347 this.xhr = new XMLHttpRequest();
27349 this.managerEl.createChild({
27351 cls : 'roo-document-manager-loading',
27355 tooltip : file.name,
27356 cls : 'roo-document-manager-thumb',
27357 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27363 this.xhr.open(this.method, this.url, true);
27366 "Accept": "application/json",
27367 "Cache-Control": "no-cache",
27368 "X-Requested-With": "XMLHttpRequest"
27371 for (var headerName in headers) {
27372 var headerValue = headers[headerName];
27374 this.xhr.setRequestHeader(headerName, headerValue);
27380 this.xhr.onload = function()
27382 _this.xhrOnLoad(_this.xhr);
27385 this.xhr.onerror = function()
27387 _this.xhrOnError(_this.xhr);
27390 var formData = new FormData();
27392 formData.append('returnHTML', 'NO');
27394 formData.append('crop', crop);
27396 if(typeof(file.filename) != 'undefined'){
27397 formData.append('filename', file.filename);
27400 if(typeof(file.mimetype) != 'undefined'){
27401 formData.append('mimetype', file.mimetype);
27404 if(this.fireEvent('prepare', this, formData) != false){
27405 this.xhr.send(formData);
27415 * @class Roo.bootstrap.DocumentViewer
27416 * @extends Roo.bootstrap.Component
27417 * Bootstrap DocumentViewer class
27420 * Create a new DocumentViewer
27421 * @param {Object} config The config object
27424 Roo.bootstrap.DocumentViewer = function(config){
27425 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27430 * Fire after initEvent
27431 * @param {Roo.bootstrap.DocumentViewer} this
27437 * @param {Roo.bootstrap.DocumentViewer} this
27442 * Fire after trash button
27443 * @param {Roo.bootstrap.DocumentViewer} this
27450 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27452 getAutoCreate : function()
27456 cls : 'roo-document-viewer',
27460 cls : 'roo-document-viewer-body',
27464 cls : 'roo-document-viewer-thumb',
27468 cls : 'roo-document-viewer-image'
27476 cls : 'roo-document-viewer-footer',
27479 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27487 cls : 'btn btn-default roo-document-viewer-trash',
27488 html : '<i class="fa fa-trash"></i>'
27501 initEvents : function()
27504 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27505 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27507 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27508 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27510 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27511 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27513 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27514 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27516 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27517 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27519 this.bodyEl.on('click', this.onClick, this);
27521 this.trashBtn.on('click', this.onTrash, this);
27525 initial : function()
27527 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27530 this.fireEvent('initial', this);
27534 onClick : function(e)
27536 e.preventDefault();
27538 this.fireEvent('click', this);
27541 onTrash : function(e)
27543 e.preventDefault();
27545 this.fireEvent('trash', this);
27557 * @class Roo.bootstrap.NavProgressBar
27558 * @extends Roo.bootstrap.Component
27559 * Bootstrap NavProgressBar class
27562 * Create a new nav progress bar
27563 * @param {Object} config The config object
27566 Roo.bootstrap.NavProgressBar = function(config){
27567 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27569 this.bullets = this.bullets || [];
27571 // Roo.bootstrap.NavProgressBar.register(this);
27575 * Fires when the active item changes
27576 * @param {Roo.bootstrap.NavProgressBar} this
27577 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27578 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27585 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27590 getAutoCreate : function()
27592 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27596 cls : 'roo-navigation-bar-group',
27600 cls : 'roo-navigation-top-bar'
27604 cls : 'roo-navigation-bullets-bar',
27608 cls : 'roo-navigation-bar'
27615 cls : 'roo-navigation-bottom-bar'
27625 initEvents: function()
27630 onRender : function(ct, position)
27632 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27634 if(this.bullets.length){
27635 Roo.each(this.bullets, function(b){
27644 addItem : function(cfg)
27646 var item = new Roo.bootstrap.NavProgressItem(cfg);
27648 item.parentId = this.id;
27649 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27652 var top = new Roo.bootstrap.Element({
27654 cls : 'roo-navigation-bar-text'
27657 var bottom = new Roo.bootstrap.Element({
27659 cls : 'roo-navigation-bar-text'
27662 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27663 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27665 var topText = new Roo.bootstrap.Element({
27667 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27670 var bottomText = new Roo.bootstrap.Element({
27672 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27675 topText.onRender(top.el, null);
27676 bottomText.onRender(bottom.el, null);
27679 item.bottomEl = bottom;
27682 this.barItems.push(item);
27687 getActive : function()
27689 var active = false;
27691 Roo.each(this.barItems, function(v){
27693 if (!v.isActive()) {
27705 setActiveItem : function(item)
27709 Roo.each(this.barItems, function(v){
27710 if (v.rid == item.rid) {
27714 if (v.isActive()) {
27715 v.setActive(false);
27720 item.setActive(true);
27722 this.fireEvent('changed', this, item, prev);
27725 getBarItem: function(rid)
27729 Roo.each(this.barItems, function(e) {
27730 if (e.rid != rid) {
27741 indexOfItem : function(item)
27745 Roo.each(this.barItems, function(v, i){
27747 if (v.rid != item.rid) {
27758 setActiveNext : function()
27760 var i = this.indexOfItem(this.getActive());
27762 if (i > this.barItems.length) {
27766 this.setActiveItem(this.barItems[i+1]);
27769 setActivePrev : function()
27771 var i = this.indexOfItem(this.getActive());
27777 this.setActiveItem(this.barItems[i-1]);
27780 format : function()
27782 if(!this.barItems.length){
27786 var width = 100 / this.barItems.length;
27788 Roo.each(this.barItems, function(i){
27789 i.el.setStyle('width', width + '%');
27790 i.topEl.el.setStyle('width', width + '%');
27791 i.bottomEl.el.setStyle('width', width + '%');
27800 * Nav Progress Item
27805 * @class Roo.bootstrap.NavProgressItem
27806 * @extends Roo.bootstrap.Component
27807 * Bootstrap NavProgressItem class
27808 * @cfg {String} rid the reference id
27809 * @cfg {Boolean} active (true|false) Is item active default false
27810 * @cfg {Boolean} disabled (true|false) Is item active default false
27811 * @cfg {String} html
27812 * @cfg {String} position (top|bottom) text position default bottom
27813 * @cfg {String} icon show icon instead of number
27816 * Create a new NavProgressItem
27817 * @param {Object} config The config object
27819 Roo.bootstrap.NavProgressItem = function(config){
27820 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27825 * The raw click event for the entire grid.
27826 * @param {Roo.bootstrap.NavProgressItem} this
27827 * @param {Roo.EventObject} e
27834 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27840 position : 'bottom',
27843 getAutoCreate : function()
27845 var iconCls = 'roo-navigation-bar-item-icon';
27847 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27851 cls: 'roo-navigation-bar-item',
27861 cfg.cls += ' active';
27864 cfg.cls += ' disabled';
27870 disable : function()
27872 this.setDisabled(true);
27875 enable : function()
27877 this.setDisabled(false);
27880 initEvents: function()
27882 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27884 this.iconEl.on('click', this.onClick, this);
27887 onClick : function(e)
27889 e.preventDefault();
27895 if(this.fireEvent('click', this, e) === false){
27899 this.parent().setActiveItem(this);
27902 isActive: function ()
27904 return this.active;
27907 setActive : function(state)
27909 if(this.active == state){
27913 this.active = state;
27916 this.el.addClass('active');
27920 this.el.removeClass('active');
27925 setDisabled : function(state)
27927 if(this.disabled == state){
27931 this.disabled = state;
27934 this.el.addClass('disabled');
27938 this.el.removeClass('disabled');
27941 tooltipEl : function()
27943 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27956 * @class Roo.bootstrap.FieldLabel
27957 * @extends Roo.bootstrap.Component
27958 * Bootstrap FieldLabel class
27959 * @cfg {String} html contents of the element
27960 * @cfg {String} tag tag of the element default label
27961 * @cfg {String} cls class of the element
27962 * @cfg {String} target label target
27963 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27964 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27965 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27966 * @cfg {String} iconTooltip default "This field is required"
27969 * Create a new FieldLabel
27970 * @param {Object} config The config object
27973 Roo.bootstrap.FieldLabel = function(config){
27974 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27979 * Fires after the field has been marked as invalid.
27980 * @param {Roo.form.FieldLabel} this
27981 * @param {String} msg The validation message
27986 * Fires after the field has been validated with no errors.
27987 * @param {Roo.form.FieldLabel} this
27993 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28000 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28001 validClass : 'text-success fa fa-lg fa-check',
28002 iconTooltip : 'This field is required',
28004 getAutoCreate : function(){
28008 cls : 'roo-bootstrap-field-label ' + this.cls,
28014 tooltip : this.iconTooltip
28026 initEvents: function()
28028 Roo.bootstrap.Element.superclass.initEvents.call(this);
28030 this.iconEl = this.el.select('i', true).first();
28032 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28034 Roo.bootstrap.FieldLabel.register(this);
28038 * Mark this field as valid
28040 markValid : function()
28042 this.iconEl.show();
28044 this.iconEl.removeClass(this.invalidClass);
28046 this.iconEl.addClass(this.validClass);
28048 this.fireEvent('valid', this);
28052 * Mark this field as invalid
28053 * @param {String} msg The validation message
28055 markInvalid : function(msg)
28057 this.iconEl.show();
28059 this.iconEl.removeClass(this.validClass);
28061 this.iconEl.addClass(this.invalidClass);
28063 this.fireEvent('invalid', this, msg);
28069 Roo.apply(Roo.bootstrap.FieldLabel, {
28074 * register a FieldLabel Group
28075 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28077 register : function(label)
28079 if(this.groups.hasOwnProperty(label.target)){
28083 this.groups[label.target] = label;
28087 * fetch a FieldLabel Group based on the target
28088 * @param {string} target
28089 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28091 get: function(target) {
28092 if (typeof(this.groups[target]) == 'undefined') {
28096 return this.groups[target] ;
28105 * page DateSplitField.
28111 * @class Roo.bootstrap.DateSplitField
28112 * @extends Roo.bootstrap.Component
28113 * Bootstrap DateSplitField class
28114 * @cfg {string} fieldLabel - the label associated
28115 * @cfg {Number} labelWidth set the width of label (0-12)
28116 * @cfg {String} labelAlign (top|left)
28117 * @cfg {Boolean} dayAllowBlank (true|false) default false
28118 * @cfg {Boolean} monthAllowBlank (true|false) default false
28119 * @cfg {Boolean} yearAllowBlank (true|false) default false
28120 * @cfg {string} dayPlaceholder
28121 * @cfg {string} monthPlaceholder
28122 * @cfg {string} yearPlaceholder
28123 * @cfg {string} dayFormat default 'd'
28124 * @cfg {string} monthFormat default 'm'
28125 * @cfg {string} yearFormat default 'Y'
28129 * Create a new DateSplitField
28130 * @param {Object} config The config object
28133 Roo.bootstrap.DateSplitField = function(config){
28134 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28140 * getting the data of years
28141 * @param {Roo.bootstrap.DateSplitField} this
28142 * @param {Object} years
28147 * getting the data of days
28148 * @param {Roo.bootstrap.DateSplitField} this
28149 * @param {Object} days
28154 * Fires after the field has been marked as invalid.
28155 * @param {Roo.form.Field} this
28156 * @param {String} msg The validation message
28161 * Fires after the field has been validated with no errors.
28162 * @param {Roo.form.Field} this
28168 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28171 labelAlign : 'top',
28173 dayAllowBlank : false,
28174 monthAllowBlank : false,
28175 yearAllowBlank : false,
28176 dayPlaceholder : '',
28177 monthPlaceholder : '',
28178 yearPlaceholder : '',
28182 isFormField : true,
28184 getAutoCreate : function()
28188 cls : 'row roo-date-split-field-group',
28193 cls : 'form-hidden-field roo-date-split-field-group-value',
28199 if(this.fieldLabel){
28202 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28206 html : this.fieldLabel
28212 Roo.each(['day', 'month', 'year'], function(t){
28215 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28222 inputEl: function ()
28224 return this.el.select('.roo-date-split-field-group-value', true).first();
28227 onRender : function(ct, position)
28231 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28233 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28235 this.dayField = new Roo.bootstrap.ComboBox({
28236 allowBlank : this.dayAllowBlank,
28237 alwaysQuery : true,
28238 displayField : 'value',
28241 forceSelection : true,
28243 placeholder : this.dayPlaceholder,
28244 selectOnFocus : true,
28245 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28246 triggerAction : 'all',
28248 valueField : 'value',
28249 store : new Roo.data.SimpleStore({
28250 data : (function() {
28252 _this.fireEvent('days', _this, days);
28255 fields : [ 'value' ]
28258 select : function (_self, record, index)
28260 _this.setValue(_this.getValue());
28265 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28267 this.monthField = new Roo.bootstrap.MonthField({
28268 after : '<i class=\"fa fa-calendar\"></i>',
28269 allowBlank : this.monthAllowBlank,
28270 placeholder : this.monthPlaceholder,
28273 render : function (_self)
28275 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28276 e.preventDefault();
28280 select : function (_self, oldvalue, newvalue)
28282 _this.setValue(_this.getValue());
28287 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28289 this.yearField = new Roo.bootstrap.ComboBox({
28290 allowBlank : this.yearAllowBlank,
28291 alwaysQuery : true,
28292 displayField : 'value',
28295 forceSelection : true,
28297 placeholder : this.yearPlaceholder,
28298 selectOnFocus : true,
28299 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28300 triggerAction : 'all',
28302 valueField : 'value',
28303 store : new Roo.data.SimpleStore({
28304 data : (function() {
28306 _this.fireEvent('years', _this, years);
28309 fields : [ 'value' ]
28312 select : function (_self, record, index)
28314 _this.setValue(_this.getValue());
28319 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28322 setValue : function(v, format)
28324 this.inputEl.dom.value = v;
28326 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28328 var d = Date.parseDate(v, f);
28335 this.setDay(d.format(this.dayFormat));
28336 this.setMonth(d.format(this.monthFormat));
28337 this.setYear(d.format(this.yearFormat));
28344 setDay : function(v)
28346 this.dayField.setValue(v);
28347 this.inputEl.dom.value = this.getValue();
28352 setMonth : function(v)
28354 this.monthField.setValue(v, true);
28355 this.inputEl.dom.value = this.getValue();
28360 setYear : function(v)
28362 this.yearField.setValue(v);
28363 this.inputEl.dom.value = this.getValue();
28368 getDay : function()
28370 return this.dayField.getValue();
28373 getMonth : function()
28375 return this.monthField.getValue();
28378 getYear : function()
28380 return this.yearField.getValue();
28383 getValue : function()
28385 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28387 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28397 this.inputEl.dom.value = '';
28402 validate : function()
28404 var d = this.dayField.validate();
28405 var m = this.monthField.validate();
28406 var y = this.yearField.validate();
28411 (!this.dayAllowBlank && !d) ||
28412 (!this.monthAllowBlank && !m) ||
28413 (!this.yearAllowBlank && !y)
28418 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28427 this.markInvalid();
28432 markValid : function()
28435 var label = this.el.select('label', true).first();
28436 var icon = this.el.select('i.fa-star', true).first();
28442 this.fireEvent('valid', this);
28446 * Mark this field as invalid
28447 * @param {String} msg The validation message
28449 markInvalid : function(msg)
28452 var label = this.el.select('label', true).first();
28453 var icon = this.el.select('i.fa-star', true).first();
28455 if(label && !icon){
28456 this.el.select('.roo-date-split-field-label', true).createChild({
28458 cls : 'text-danger fa fa-lg fa-star',
28459 tooltip : 'This field is required',
28460 style : 'margin-right:5px;'
28464 this.fireEvent('invalid', this, msg);
28467 clearInvalid : function()
28469 var label = this.el.select('label', true).first();
28470 var icon = this.el.select('i.fa-star', true).first();
28476 this.fireEvent('valid', this);
28479 getName: function()
28489 * http://masonry.desandro.com
28491 * The idea is to render all the bricks based on vertical width...
28493 * The original code extends 'outlayer' - we might need to use that....
28499 * @class Roo.bootstrap.LayoutMasonry
28500 * @extends Roo.bootstrap.Component
28501 * Bootstrap Layout Masonry class
28504 * Create a new Element
28505 * @param {Object} config The config object
28508 Roo.bootstrap.LayoutMasonry = function(config){
28509 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28515 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
28518 * @cfg {Boolean} isLayoutInstant = no animation?
28520 isLayoutInstant : false, // needed?
28523 * @cfg {Number} boxWidth width of the columns
28528 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
28533 * @cfg {Number} padWidth padding below box..
28538 * @cfg {Number} gutter gutter width..
28543 * @cfg {Number} maxCols maximum number of columns
28549 * @cfg {Boolean} isAutoInitial defalut true
28551 isAutoInitial : true,
28556 * @cfg {Boolean} isHorizontal defalut false
28558 isHorizontal : false,
28560 currentSize : null,
28566 bricks: null, //CompositeElement
28570 _isLayoutInited : false,
28572 // isAlternative : false, // only use for vertical layout...
28575 * @cfg {Number} alternativePadWidth padding below box..
28577 alternativePadWidth : 50,
28579 getAutoCreate : function(){
28583 cls: 'blog-masonary-wrapper ' + this.cls,
28585 cls : 'mas-boxes masonary'
28592 getChildContainer: function( )
28594 if (this.boxesEl) {
28595 return this.boxesEl;
28598 this.boxesEl = this.el.select('.mas-boxes').first();
28600 return this.boxesEl;
28604 initEvents : function()
28608 if(this.isAutoInitial){
28609 Roo.log('hook children rendered');
28610 this.on('childrenrendered', function() {
28611 Roo.log('children rendered');
28617 initial : function()
28619 this.currentSize = this.el.getBox(true);
28621 Roo.EventManager.onWindowResize(this.resize, this);
28623 if(!this.isAutoInitial){
28631 //this.layout.defer(500,this);
28635 resize : function()
28639 var cs = this.el.getBox(true);
28641 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28642 Roo.log("no change in with or X");
28646 this.currentSize = cs;
28652 layout : function()
28654 this._resetLayout();
28656 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28658 this.layoutItems( isInstant );
28660 this._isLayoutInited = true;
28664 _resetLayout : function()
28666 if(this.isHorizontal){
28667 this.horizontalMeasureColumns();
28671 this.verticalMeasureColumns();
28675 verticalMeasureColumns : function()
28677 this.getContainerWidth();
28679 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28680 // this.colWidth = Math.floor(this.containerWidth * 0.8);
28684 var boxWidth = this.boxWidth + this.padWidth;
28686 if(this.containerWidth < this.boxWidth){
28687 boxWidth = this.containerWidth
28690 var containerWidth = this.containerWidth;
28692 var cols = Math.floor(containerWidth / boxWidth);
28694 this.cols = Math.max( cols, 1 );
28696 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28698 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28700 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28702 this.colWidth = boxWidth + avail - this.padWidth;
28704 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28705 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
28708 horizontalMeasureColumns : function()
28710 this.getContainerWidth();
28712 var boxWidth = this.boxWidth;
28714 if(this.containerWidth < boxWidth){
28715 boxWidth = this.containerWidth;
28718 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28720 this.el.setHeight(boxWidth);
28724 getContainerWidth : function()
28726 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
28729 layoutItems : function( isInstant )
28731 var items = Roo.apply([], this.bricks);
28733 if(this.isHorizontal){
28734 this._horizontalLayoutItems( items , isInstant );
28738 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28739 // this._verticalAlternativeLayoutItems( items , isInstant );
28743 this._verticalLayoutItems( items , isInstant );
28747 _verticalLayoutItems : function ( items , isInstant)
28749 if ( !items || !items.length ) {
28754 ['xs', 'xs', 'xs', 'tall'],
28755 ['xs', 'xs', 'tall'],
28756 ['xs', 'xs', 'sm'],
28757 ['xs', 'xs', 'xs'],
28763 ['sm', 'xs', 'xs'],
28767 ['tall', 'xs', 'xs', 'xs'],
28768 ['tall', 'xs', 'xs'],
28780 Roo.each(items, function(item, k){
28782 switch (item.size) {
28783 // these layouts take up a full box,
28794 boxes.push([item]);
28817 var filterPattern = function(box, length)
28825 var pattern = box.slice(0, length);
28829 Roo.each(pattern, function(i){
28830 format.push(i.size);
28833 Roo.each(standard, function(s){
28835 if(String(s) != String(format)){
28844 if(!match && length == 1){
28849 filterPattern(box, length - 1);
28853 queue.push(pattern);
28855 box = box.slice(length, box.length);
28857 filterPattern(box, 4);
28863 Roo.each(boxes, function(box, k){
28869 if(box.length == 1){
28874 filterPattern(box, 4);
28878 this._processVerticalLayoutQueue( queue, isInstant );
28882 // _verticalAlternativeLayoutItems : function( items , isInstant )
28884 // if ( !items || !items.length ) {
28888 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
28892 _horizontalLayoutItems : function ( items , isInstant)
28894 if ( !items || !items.length || items.length < 3) {
28900 var eItems = items.slice(0, 3);
28902 items = items.slice(3, items.length);
28905 ['xs', 'xs', 'xs', 'wide'],
28906 ['xs', 'xs', 'wide'],
28907 ['xs', 'xs', 'sm'],
28908 ['xs', 'xs', 'xs'],
28914 ['sm', 'xs', 'xs'],
28918 ['wide', 'xs', 'xs', 'xs'],
28919 ['wide', 'xs', 'xs'],
28932 Roo.each(items, function(item, k){
28934 switch (item.size) {
28945 boxes.push([item]);
28969 var filterPattern = function(box, length)
28977 var pattern = box.slice(0, length);
28981 Roo.each(pattern, function(i){
28982 format.push(i.size);
28985 Roo.each(standard, function(s){
28987 if(String(s) != String(format)){
28996 if(!match && length == 1){
29001 filterPattern(box, length - 1);
29005 queue.push(pattern);
29007 box = box.slice(length, box.length);
29009 filterPattern(box, 4);
29015 Roo.each(boxes, function(box, k){
29021 if(box.length == 1){
29026 filterPattern(box, 4);
29033 var pos = this.el.getBox(true);
29037 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29039 var hit_end = false;
29041 Roo.each(queue, function(box){
29045 Roo.each(box, function(b){
29047 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29057 Roo.each(box, function(b){
29059 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29062 mx = Math.max(mx, b.x);
29066 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29070 Roo.each(box, function(b){
29072 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29086 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29089 /** Sets position of item in DOM
29090 * @param {Element} item
29091 * @param {Number} x - horizontal position
29092 * @param {Number} y - vertical position
29093 * @param {Boolean} isInstant - disables transitions
29095 _processVerticalLayoutQueue : function( queue, isInstant )
29097 var pos = this.el.getBox(true);
29102 for (var i = 0; i < this.cols; i++){
29106 Roo.each(queue, function(box, k){
29108 var col = k % this.cols;
29110 Roo.each(box, function(b,kk){
29112 b.el.position('absolute');
29114 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29115 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29117 if(b.size == 'md-left' || b.size == 'md-right'){
29118 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29119 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29122 b.el.setWidth(width);
29123 b.el.setHeight(height);
29125 b.el.select('iframe',true).setSize(width,height);
29129 for (var i = 0; i < this.cols; i++){
29131 if(maxY[i] < maxY[col]){
29136 col = Math.min(col, i);
29140 x = pos.x + col * (this.colWidth + this.padWidth);
29144 var positions = [];
29146 switch (box.length){
29148 positions = this.getVerticalOneBoxColPositions(x, y, box);
29151 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29154 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29157 positions = this.getVerticalFourBoxColPositions(x, y, box);
29163 Roo.each(box, function(b,kk){
29165 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29167 var sz = b.el.getSize();
29169 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29177 for (var i = 0; i < this.cols; i++){
29178 mY = Math.max(mY, maxY[i]);
29181 this.el.setHeight(mY - pos.y);
29185 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29187 // var pos = this.el.getBox(true);
29190 // var maxX = pos.right;
29192 // var maxHeight = 0;
29194 // Roo.each(items, function(item, k){
29198 // item.el.position('absolute');
29200 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29202 // item.el.setWidth(width);
29204 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29206 // item.el.setHeight(height);
29209 // item.el.setXY([x, y], isInstant ? false : true);
29211 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29214 // y = y + height + this.alternativePadWidth;
29216 // maxHeight = maxHeight + height + this.alternativePadWidth;
29220 // this.el.setHeight(maxHeight);
29224 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29226 var pos = this.el.getBox(true);
29231 var maxX = pos.right;
29233 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29235 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29237 Roo.each(queue, function(box, k){
29239 Roo.each(box, function(b, kk){
29241 b.el.position('absolute');
29243 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29244 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29246 if(b.size == 'md-left' || b.size == 'md-right'){
29247 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29248 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29251 b.el.setWidth(width);
29252 b.el.setHeight(height);
29260 var positions = [];
29262 switch (box.length){
29264 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29267 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29270 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29273 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29279 Roo.each(box, function(b,kk){
29281 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29283 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29291 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29293 Roo.each(eItems, function(b,k){
29295 b.size = (k == 0) ? 'sm' : 'xs';
29296 b.x = (k == 0) ? 2 : 1;
29297 b.y = (k == 0) ? 2 : 1;
29299 b.el.position('absolute');
29301 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29303 b.el.setWidth(width);
29305 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29307 b.el.setHeight(height);
29311 var positions = [];
29314 x : maxX - this.unitWidth * 2 - this.gutter,
29319 x : maxX - this.unitWidth,
29320 y : minY + (this.unitWidth + this.gutter) * 2
29324 x : maxX - this.unitWidth * 3 - this.gutter * 2,
29328 Roo.each(eItems, function(b,k){
29330 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29336 getVerticalOneBoxColPositions : function(x, y, box)
29340 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29342 if(box[0].size == 'md-left'){
29346 if(box[0].size == 'md-right'){
29351 x : x + (this.unitWidth + this.gutter) * rand,
29358 getVerticalTwoBoxColPositions : function(x, y, box)
29362 if(box[0].size == 'xs'){
29366 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29370 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29384 x : x + (this.unitWidth + this.gutter) * 2,
29385 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29392 getVerticalThreeBoxColPositions : function(x, y, box)
29396 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29404 x : x + (this.unitWidth + this.gutter) * 1,
29409 x : x + (this.unitWidth + this.gutter) * 2,
29417 if(box[0].size == 'xs' && box[1].size == 'xs'){
29426 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29430 x : x + (this.unitWidth + this.gutter) * 1,
29444 x : x + (this.unitWidth + this.gutter) * 2,
29449 x : x + (this.unitWidth + this.gutter) * 2,
29450 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29457 getVerticalFourBoxColPositions : function(x, y, box)
29461 if(box[0].size == 'xs'){
29470 y : y + (this.unitHeight + this.gutter) * 1
29475 y : y + (this.unitHeight + this.gutter) * 2
29479 x : x + (this.unitWidth + this.gutter) * 1,
29493 x : x + (this.unitWidth + this.gutter) * 2,
29498 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29499 y : y + (this.unitHeight + this.gutter) * 1
29503 x : x + (this.unitWidth + this.gutter) * 2,
29504 y : y + (this.unitWidth + this.gutter) * 2
29511 getHorizontalOneBoxColPositions : function(maxX, minY, box)
29515 if(box[0].size == 'md-left'){
29517 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29524 if(box[0].size == 'md-right'){
29526 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29527 y : minY + (this.unitWidth + this.gutter) * 1
29533 var rand = Math.floor(Math.random() * (4 - box[0].y));
29536 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29537 y : minY + (this.unitWidth + this.gutter) * rand
29544 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29548 if(box[0].size == 'xs'){
29551 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29556 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29557 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29565 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29570 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29571 y : minY + (this.unitWidth + this.gutter) * 2
29578 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29582 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29585 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29590 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29591 y : minY + (this.unitWidth + this.gutter) * 1
29595 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29596 y : minY + (this.unitWidth + this.gutter) * 2
29603 if(box[0].size == 'xs' && box[1].size == 'xs'){
29606 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29611 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29616 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29617 y : minY + (this.unitWidth + this.gutter) * 1
29625 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29630 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29631 y : minY + (this.unitWidth + this.gutter) * 2
29635 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29636 y : minY + (this.unitWidth + this.gutter) * 2
29643 getHorizontalFourBoxColPositions : function(maxX, minY, box)
29647 if(box[0].size == 'xs'){
29650 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29655 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29660 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),
29665 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29666 y : minY + (this.unitWidth + this.gutter) * 1
29674 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29679 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29680 y : minY + (this.unitWidth + this.gutter) * 2
29684 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29685 y : minY + (this.unitWidth + this.gutter) * 2
29689 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),
29690 y : minY + (this.unitWidth + this.gutter) * 2
29704 * http://masonry.desandro.com
29706 * The idea is to render all the bricks based on vertical width...
29708 * The original code extends 'outlayer' - we might need to use that....
29714 * @class Roo.bootstrap.LayoutMasonryAuto
29715 * @extends Roo.bootstrap.Component
29716 * Bootstrap Layout Masonry class
29719 * Create a new Element
29720 * @param {Object} config The config object
29723 Roo.bootstrap.LayoutMasonryAuto = function(config){
29724 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29727 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
29730 * @cfg {Boolean} isFitWidth - resize the width..
29732 isFitWidth : false, // options..
29734 * @cfg {Boolean} isOriginLeft = left align?
29736 isOriginLeft : true,
29738 * @cfg {Boolean} isOriginTop = top align?
29740 isOriginTop : false,
29742 * @cfg {Boolean} isLayoutInstant = no animation?
29744 isLayoutInstant : false, // needed?
29746 * @cfg {Boolean} isResizingContainer = not sure if this is used..
29748 isResizingContainer : true,
29750 * @cfg {Number} columnWidth width of the columns
29756 * @cfg {Number} maxCols maximum number of columns
29761 * @cfg {Number} padHeight padding below box..
29767 * @cfg {Boolean} isAutoInitial defalut true
29770 isAutoInitial : true,
29776 initialColumnWidth : 0,
29777 currentSize : null,
29779 colYs : null, // array.
29786 bricks: null, //CompositeElement
29787 cols : 0, // array?
29788 // element : null, // wrapped now this.el
29789 _isLayoutInited : null,
29792 getAutoCreate : function(){
29796 cls: 'blog-masonary-wrapper ' + this.cls,
29798 cls : 'mas-boxes masonary'
29805 getChildContainer: function( )
29807 if (this.boxesEl) {
29808 return this.boxesEl;
29811 this.boxesEl = this.el.select('.mas-boxes').first();
29813 return this.boxesEl;
29817 initEvents : function()
29821 if(this.isAutoInitial){
29822 Roo.log('hook children rendered');
29823 this.on('childrenrendered', function() {
29824 Roo.log('children rendered');
29831 initial : function()
29833 this.reloadItems();
29835 this.currentSize = this.el.getBox(true);
29837 /// was window resize... - let's see if this works..
29838 Roo.EventManager.onWindowResize(this.resize, this);
29840 if(!this.isAutoInitial){
29845 this.layout.defer(500,this);
29848 reloadItems: function()
29850 this.bricks = this.el.select('.masonry-brick', true);
29852 this.bricks.each(function(b) {
29853 //Roo.log(b.getSize());
29854 if (!b.attr('originalwidth')) {
29855 b.attr('originalwidth', b.getSize().width);
29860 Roo.log(this.bricks.elements.length);
29863 resize : function()
29866 var cs = this.el.getBox(true);
29868 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29869 Roo.log("no change in with or X");
29872 this.currentSize = cs;
29876 layout : function()
29879 this._resetLayout();
29880 //this._manageStamps();
29882 // don't animate first layout
29883 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29884 this.layoutItems( isInstant );
29886 // flag for initalized
29887 this._isLayoutInited = true;
29890 layoutItems : function( isInstant )
29892 //var items = this._getItemsForLayout( this.items );
29893 // original code supports filtering layout items.. we just ignore it..
29895 this._layoutItems( this.bricks , isInstant );
29897 this._postLayout();
29899 _layoutItems : function ( items , isInstant)
29901 //this.fireEvent( 'layout', this, items );
29904 if ( !items || !items.elements.length ) {
29905 // no items, emit event with empty array
29910 items.each(function(item) {
29911 Roo.log("layout item");
29913 // get x/y object from method
29914 var position = this._getItemLayoutPosition( item );
29916 position.item = item;
29917 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29918 queue.push( position );
29921 this._processLayoutQueue( queue );
29923 /** Sets position of item in DOM
29924 * @param {Element} item
29925 * @param {Number} x - horizontal position
29926 * @param {Number} y - vertical position
29927 * @param {Boolean} isInstant - disables transitions
29929 _processLayoutQueue : function( queue )
29931 for ( var i=0, len = queue.length; i < len; i++ ) {
29932 var obj = queue[i];
29933 obj.item.position('absolute');
29934 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
29940 * Any logic you want to do after each layout,
29941 * i.e. size the container
29943 _postLayout : function()
29945 this.resizeContainer();
29948 resizeContainer : function()
29950 if ( !this.isResizingContainer ) {
29953 var size = this._getContainerSize();
29955 this.el.setSize(size.width,size.height);
29956 this.boxesEl.setSize(size.width,size.height);
29962 _resetLayout : function()
29964 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
29965 this.colWidth = this.el.getWidth();
29966 //this.gutter = this.el.getWidth();
29968 this.measureColumns();
29974 this.colYs.push( 0 );
29980 measureColumns : function()
29982 this.getContainerWidth();
29983 // if columnWidth is 0, default to outerWidth of first item
29984 if ( !this.columnWidth ) {
29985 var firstItem = this.bricks.first();
29986 Roo.log(firstItem);
29987 this.columnWidth = this.containerWidth;
29988 if (firstItem && firstItem.attr('originalwidth') ) {
29989 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
29991 // columnWidth fall back to item of first element
29992 Roo.log("set column width?");
29993 this.initialColumnWidth = this.columnWidth ;
29995 // if first elem has no width, default to size of container
30000 if (this.initialColumnWidth) {
30001 this.columnWidth = this.initialColumnWidth;
30006 // column width is fixed at the top - however if container width get's smaller we should
30009 // this bit calcs how man columns..
30011 var columnWidth = this.columnWidth += this.gutter;
30013 // calculate columns
30014 var containerWidth = this.containerWidth + this.gutter;
30016 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30017 // fix rounding errors, typically with gutters
30018 var excess = columnWidth - containerWidth % columnWidth;
30021 // if overshoot is less than a pixel, round up, otherwise floor it
30022 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30023 cols = Math[ mathMethod ]( cols );
30024 this.cols = Math.max( cols, 1 );
30025 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30027 // padding positioning..
30028 var totalColWidth = this.cols * this.columnWidth;
30029 var padavail = this.containerWidth - totalColWidth;
30030 // so for 2 columns - we need 3 'pads'
30032 var padNeeded = (1+this.cols) * this.padWidth;
30034 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30036 this.columnWidth += padExtra
30037 //this.padWidth = Math.floor(padavail / ( this.cols));
30039 // adjust colum width so that padding is fixed??
30041 // we have 3 columns ... total = width * 3
30042 // we have X left over... that should be used by
30044 //if (this.expandC) {
30052 getContainerWidth : function()
30054 /* // container is parent if fit width
30055 var container = this.isFitWidth ? this.element.parentNode : this.element;
30056 // check that this.size and size are there
30057 // IE8 triggers resize on body size change, so they might not be
30059 var size = getSize( container ); //FIXME
30060 this.containerWidth = size && size.innerWidth; //FIXME
30063 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30067 _getItemLayoutPosition : function( item ) // what is item?
30069 // we resize the item to our columnWidth..
30071 item.setWidth(this.columnWidth);
30072 item.autoBoxAdjust = false;
30074 var sz = item.getSize();
30076 // how many columns does this brick span
30077 var remainder = this.containerWidth % this.columnWidth;
30079 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30080 // round if off by 1 pixel, otherwise use ceil
30081 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30082 colSpan = Math.min( colSpan, this.cols );
30084 // normally this should be '1' as we dont' currently allow multi width columns..
30086 var colGroup = this._getColGroup( colSpan );
30087 // get the minimum Y value from the columns
30088 var minimumY = Math.min.apply( Math, colGroup );
30089 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30091 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30093 // position the brick
30095 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30096 y: this.currentSize.y + minimumY + this.padHeight
30100 // apply setHeight to necessary columns
30101 var setHeight = minimumY + sz.height + this.padHeight;
30102 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30104 var setSpan = this.cols + 1 - colGroup.length;
30105 for ( var i = 0; i < setSpan; i++ ) {
30106 this.colYs[ shortColIndex + i ] = setHeight ;
30113 * @param {Number} colSpan - number of columns the element spans
30114 * @returns {Array} colGroup
30116 _getColGroup : function( colSpan )
30118 if ( colSpan < 2 ) {
30119 // if brick spans only one column, use all the column Ys
30124 // how many different places could this brick fit horizontally
30125 var groupCount = this.cols + 1 - colSpan;
30126 // for each group potential horizontal position
30127 for ( var i = 0; i < groupCount; i++ ) {
30128 // make an array of colY values for that one group
30129 var groupColYs = this.colYs.slice( i, i + colSpan );
30130 // and get the max value of the array
30131 colGroup[i] = Math.max.apply( Math, groupColYs );
30136 _manageStamp : function( stamp )
30138 var stampSize = stamp.getSize();
30139 var offset = stamp.getBox();
30140 // get the columns that this stamp affects
30141 var firstX = this.isOriginLeft ? offset.x : offset.right;
30142 var lastX = firstX + stampSize.width;
30143 var firstCol = Math.floor( firstX / this.columnWidth );
30144 firstCol = Math.max( 0, firstCol );
30146 var lastCol = Math.floor( lastX / this.columnWidth );
30147 // lastCol should not go over if multiple of columnWidth #425
30148 lastCol -= lastX % this.columnWidth ? 0 : 1;
30149 lastCol = Math.min( this.cols - 1, lastCol );
30151 // set colYs to bottom of the stamp
30152 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30155 for ( var i = firstCol; i <= lastCol; i++ ) {
30156 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30161 _getContainerSize : function()
30163 this.maxY = Math.max.apply( Math, this.colYs );
30168 if ( this.isFitWidth ) {
30169 size.width = this._getContainerFitWidth();
30175 _getContainerFitWidth : function()
30177 var unusedCols = 0;
30178 // count unused columns
30181 if ( this.colYs[i] !== 0 ) {
30186 // fit container to columns that have been used
30187 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30190 needsResizeLayout : function()
30192 var previousWidth = this.containerWidth;
30193 this.getContainerWidth();
30194 return previousWidth !== this.containerWidth;
30209 * @class Roo.bootstrap.MasonryBrick
30210 * @extends Roo.bootstrap.Component
30211 * Bootstrap MasonryBrick class
30214 * Create a new MasonryBrick
30215 * @param {Object} config The config object
30218 Roo.bootstrap.MasonryBrick = function(config){
30219 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30225 * When a MasonryBrick is clcik
30226 * @param {Roo.bootstrap.MasonryBrick} this
30227 * @param {Roo.EventObject} e
30233 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30236 * @cfg {String} title
30240 * @cfg {String} html
30244 * @cfg {String} bgimage
30248 * @cfg {String} videourl
30252 * @cfg {String} cls
30256 * @cfg {String} href
30260 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30265 * @cfg {String} (center|bottom) placetitle
30269 getAutoCreate : function()
30271 var cls = 'masonry-brick';
30273 if(this.href.length){
30274 cls += ' masonry-brick-link';
30277 if(this.bgimage.length){
30278 cls += ' masonry-brick-image';
30282 cls += ' masonry-' + this.size + '-brick';
30285 if(this.placetitle.length){
30287 switch (this.placetitle) {
30289 cls += ' masonry-center-title';
30292 cls += ' masonry-bottom-title';
30299 if(!this.html.length && !this.bgimage.length){
30300 cls += ' masonry-center-title';
30303 if(!this.html.length && this.bgimage.length){
30304 cls += ' masonry-bottom-title';
30309 cls += ' ' + this.cls;
30313 tag: (this.href.length) ? 'a' : 'div',
30318 cls: 'masonry-brick-paragraph',
30324 if(this.href.length){
30325 cfg.href = this.href;
30328 var cn = cfg.cn[0].cn;
30330 if(this.title.length){
30333 cls: 'masonry-brick-title',
30338 if(this.html.length){
30341 cls: 'masonry-brick-text',
30345 if (!this.title.length && !this.html.length) {
30346 cfg.cn[0].cls += ' hide';
30349 if(this.bgimage.length){
30352 cls: 'masonry-brick-image-view',
30356 if(this.videourl.length){
30357 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30358 // youtube support only?
30361 cls: 'masonry-brick-image-view',
30364 allowfullscreen : true
30373 initEvents: function()
30375 switch (this.size) {
30377 // this.intSize = 1;
30382 // this.intSize = 2;
30389 // this.intSize = 3;
30394 // this.intSize = 3;
30399 // this.intSize = 3;
30404 // this.intSize = 3;
30416 this.el.on('touchstart', this.onTouchStart, this);
30417 this.el.on('touchmove', this.onTouchMove, this);
30418 this.el.on('touchend', this.onTouchEnd, this);
30419 this.el.on('contextmenu', this.onContextMenu, this);
30421 this.el.on('mouseenter' ,this.enter, this);
30422 this.el.on('mouseleave', this.leave, this);
30425 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30426 this.parent().bricks.push(this);
30431 onClick: function(e, el)
30437 var time = this.endTimer - this.startTimer;
30445 e.preventDefault();
30448 enter: function(e, el)
30450 e.preventDefault();
30452 if(this.bgimage.length && this.html.length){
30453 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30457 leave: function(e, el)
30459 e.preventDefault();
30461 if(this.bgimage.length && this.html.length){
30462 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30466 onTouchStart: function(e, el)
30468 // e.preventDefault();
30470 this.touchmoved = false;
30472 if(!this.bgimage.length || !this.html.length){
30476 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30478 this.timer = new Date().getTime();
30482 onTouchMove: function(e, el)
30484 this.touchmoved = true;
30487 onContextMenu : function(e,el)
30489 e.preventDefault();
30490 e.stopPropagation();
30494 onTouchEnd: function(e, el)
30496 // e.preventDefault();
30498 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30505 if(!this.bgimage.length || !this.html.length){
30507 if(this.href.length){
30508 window.location.href = this.href;
30514 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30516 window.location.href = this.href;
30531 * @class Roo.bootstrap.Brick
30532 * @extends Roo.bootstrap.Component
30533 * Bootstrap Brick class
30536 * Create a new Brick
30537 * @param {Object} config The config object
30540 Roo.bootstrap.Brick = function(config){
30541 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30547 * When a Brick is click
30548 * @param {Roo.bootstrap.Brick} this
30549 * @param {Roo.EventObject} e
30555 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
30558 * @cfg {String} title
30562 * @cfg {String} html
30566 * @cfg {String} bgimage
30570 * @cfg {String} cls
30574 * @cfg {String} href
30578 * @cfg {String} video
30582 * @cfg {Boolean} square
30586 getAutoCreate : function()
30588 var cls = 'roo-brick';
30590 if(this.href.length){
30591 cls += ' roo-brick-link';
30594 if(this.bgimage.length){
30595 cls += ' roo-brick-image';
30598 if(!this.html.length && !this.bgimage.length){
30599 cls += ' roo-brick-center-title';
30602 if(!this.html.length && this.bgimage.length){
30603 cls += ' roo-brick-bottom-title';
30607 cls += ' ' + this.cls;
30611 tag: (this.href.length) ? 'a' : 'div',
30616 cls: 'roo-brick-paragraph',
30622 if(this.href.length){
30623 cfg.href = this.href;
30626 var cn = cfg.cn[0].cn;
30628 if(this.title.length){
30631 cls: 'roo-brick-title',
30636 if(this.html.length){
30639 cls: 'roo-brick-text',
30646 if(this.bgimage.length){
30649 cls: 'roo-brick-image-view',
30657 initEvents: function()
30659 if(this.title.length || this.html.length){
30660 this.el.on('mouseenter' ,this.enter, this);
30661 this.el.on('mouseleave', this.leave, this);
30665 Roo.EventManager.onWindowResize(this.resize, this);
30670 resize : function()
30672 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30674 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30675 // paragraph.setHeight(paragraph.getWidth());
30677 if(this.bgimage.length){
30678 var image = this.el.select('.roo-brick-image-view', true).first();
30679 image.setWidth(paragraph.getWidth());
30680 image.setHeight(paragraph.getWidth());
30685 enter: function(e, el)
30687 e.preventDefault();
30689 if(this.bgimage.length){
30690 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30691 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30695 leave: function(e, el)
30697 e.preventDefault();
30699 if(this.bgimage.length){
30700 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30701 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30711 * Ext JS Library 1.1.1
30712 * Copyright(c) 2006-2007, Ext JS, LLC.
30714 * Originally Released Under LGPL - original licence link has changed is not relivant.
30717 * <script type="text/javascript">
30722 * @class Roo.bootstrap.SplitBar
30723 * @extends Roo.util.Observable
30724 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30728 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30729 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30730 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30731 split.minSize = 100;
30732 split.maxSize = 600;
30733 split.animate = true;
30734 split.on('moved', splitterMoved);
30737 * Create a new SplitBar
30738 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
30739 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
30740 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30741 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
30742 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30743 position of the SplitBar).
30745 Roo.bootstrap.SplitBar = function(cfg){
30750 // dragElement : elm
30751 // resizingElement: el,
30753 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30754 // placement : Roo.bootstrap.SplitBar.LEFT ,
30755 // existingProxy ???
30758 this.el = Roo.get(cfg.dragElement, true);
30759 this.el.dom.unselectable = "on";
30761 this.resizingEl = Roo.get(cfg.resizingElement, true);
30765 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30766 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30769 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30772 * The minimum size of the resizing element. (Defaults to 0)
30778 * The maximum size of the resizing element. (Defaults to 2000)
30781 this.maxSize = 2000;
30784 * Whether to animate the transition to the new size
30787 this.animate = false;
30790 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30793 this.useShim = false;
30798 if(!cfg.existingProxy){
30800 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30802 this.proxy = Roo.get(cfg.existingProxy).dom;
30805 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30808 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30811 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30814 this.dragSpecs = {};
30817 * @private The adapter to use to positon and resize elements
30819 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30820 this.adapter.init(this);
30822 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30824 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30825 this.el.addClass("roo-splitbar-h");
30828 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30829 this.el.addClass("roo-splitbar-v");
30835 * Fires when the splitter is moved (alias for {@link #event-moved})
30836 * @param {Roo.bootstrap.SplitBar} this
30837 * @param {Number} newSize the new width or height
30842 * Fires when the splitter is moved
30843 * @param {Roo.bootstrap.SplitBar} this
30844 * @param {Number} newSize the new width or height
30848 * @event beforeresize
30849 * Fires before the splitter is dragged
30850 * @param {Roo.bootstrap.SplitBar} this
30852 "beforeresize" : true,
30854 "beforeapply" : true
30857 Roo.util.Observable.call(this);
30860 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30861 onStartProxyDrag : function(x, y){
30862 this.fireEvent("beforeresize", this);
30864 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
30866 o.enableDisplayMode("block");
30867 // all splitbars share the same overlay
30868 Roo.bootstrap.SplitBar.prototype.overlay = o;
30870 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30871 this.overlay.show();
30872 Roo.get(this.proxy).setDisplayed("block");
30873 var size = this.adapter.getElementSize(this);
30874 this.activeMinSize = this.getMinimumSize();;
30875 this.activeMaxSize = this.getMaximumSize();;
30876 var c1 = size - this.activeMinSize;
30877 var c2 = Math.max(this.activeMaxSize - size, 0);
30878 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30879 this.dd.resetConstraints();
30880 this.dd.setXConstraint(
30881 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
30882 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30884 this.dd.setYConstraint(0, 0);
30886 this.dd.resetConstraints();
30887 this.dd.setXConstraint(0, 0);
30888 this.dd.setYConstraint(
30889 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
30890 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30893 this.dragSpecs.startSize = size;
30894 this.dragSpecs.startPoint = [x, y];
30895 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30899 * @private Called after the drag operation by the DDProxy
30901 onEndProxyDrag : function(e){
30902 Roo.get(this.proxy).setDisplayed(false);
30903 var endPoint = Roo.lib.Event.getXY(e);
30905 this.overlay.hide();
30908 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30909 newSize = this.dragSpecs.startSize +
30910 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30911 endPoint[0] - this.dragSpecs.startPoint[0] :
30912 this.dragSpecs.startPoint[0] - endPoint[0]
30915 newSize = this.dragSpecs.startSize +
30916 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30917 endPoint[1] - this.dragSpecs.startPoint[1] :
30918 this.dragSpecs.startPoint[1] - endPoint[1]
30921 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30922 if(newSize != this.dragSpecs.startSize){
30923 if(this.fireEvent('beforeapply', this, newSize) !== false){
30924 this.adapter.setElementSize(this, newSize);
30925 this.fireEvent("moved", this, newSize);
30926 this.fireEvent("resize", this, newSize);
30932 * Get the adapter this SplitBar uses
30933 * @return The adapter object
30935 getAdapter : function(){
30936 return this.adapter;
30940 * Set the adapter this SplitBar uses
30941 * @param {Object} adapter A SplitBar adapter object
30943 setAdapter : function(adapter){
30944 this.adapter = adapter;
30945 this.adapter.init(this);
30949 * Gets the minimum size for the resizing element
30950 * @return {Number} The minimum size
30952 getMinimumSize : function(){
30953 return this.minSize;
30957 * Sets the minimum size for the resizing element
30958 * @param {Number} minSize The minimum size
30960 setMinimumSize : function(minSize){
30961 this.minSize = minSize;
30965 * Gets the maximum size for the resizing element
30966 * @return {Number} The maximum size
30968 getMaximumSize : function(){
30969 return this.maxSize;
30973 * Sets the maximum size for the resizing element
30974 * @param {Number} maxSize The maximum size
30976 setMaximumSize : function(maxSize){
30977 this.maxSize = maxSize;
30981 * Sets the initialize size for the resizing element
30982 * @param {Number} size The initial size
30984 setCurrentSize : function(size){
30985 var oldAnimate = this.animate;
30986 this.animate = false;
30987 this.adapter.setElementSize(this, size);
30988 this.animate = oldAnimate;
30992 * Destroy this splitbar.
30993 * @param {Boolean} removeEl True to remove the element
30995 destroy : function(removeEl){
30997 this.shim.remove();
31000 this.proxy.parentNode.removeChild(this.proxy);
31008 * @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.
31010 Roo.bootstrap.SplitBar.createProxy = function(dir){
31011 var proxy = new Roo.Element(document.createElement("div"));
31012 proxy.unselectable();
31013 var cls = 'roo-splitbar-proxy';
31014 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31015 document.body.appendChild(proxy.dom);
31020 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31021 * Default Adapter. It assumes the splitter and resizing element are not positioned
31022 * elements and only gets/sets the width of the element. Generally used for table based layouts.
31024 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31027 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31028 // do nothing for now
31029 init : function(s){
31033 * Called before drag operations to get the current size of the resizing element.
31034 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31036 getElementSize : function(s){
31037 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31038 return s.resizingEl.getWidth();
31040 return s.resizingEl.getHeight();
31045 * Called after drag operations to set the size of the resizing element.
31046 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31047 * @param {Number} newSize The new size to set
31048 * @param {Function} onComplete A function to be invoked when resizing is complete
31050 setElementSize : function(s, newSize, onComplete){
31051 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31053 s.resizingEl.setWidth(newSize);
31055 onComplete(s, newSize);
31058 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31063 s.resizingEl.setHeight(newSize);
31065 onComplete(s, newSize);
31068 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31075 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31076 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31077 * Adapter that moves the splitter element to align with the resized sizing element.
31078 * Used with an absolute positioned SplitBar.
31079 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31080 * document.body, make sure you assign an id to the body element.
31082 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31083 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31084 this.container = Roo.get(container);
31087 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31088 init : function(s){
31089 this.basic.init(s);
31092 getElementSize : function(s){
31093 return this.basic.getElementSize(s);
31096 setElementSize : function(s, newSize, onComplete){
31097 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31100 moveSplitter : function(s){
31101 var yes = Roo.bootstrap.SplitBar;
31102 switch(s.placement){
31104 s.el.setX(s.resizingEl.getRight());
31107 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31110 s.el.setY(s.resizingEl.getBottom());
31113 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31120 * Orientation constant - Create a vertical SplitBar
31124 Roo.bootstrap.SplitBar.VERTICAL = 1;
31127 * Orientation constant - Create a horizontal SplitBar
31131 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31134 * Placement constant - The resizing element is to the left of the splitter element
31138 Roo.bootstrap.SplitBar.LEFT = 1;
31141 * Placement constant - The resizing element is to the right of the splitter element
31145 Roo.bootstrap.SplitBar.RIGHT = 2;
31148 * Placement constant - The resizing element is positioned above the splitter element
31152 Roo.bootstrap.SplitBar.TOP = 3;
31155 * Placement constant - The resizing element is positioned under splitter element
31159 Roo.bootstrap.SplitBar.BOTTOM = 4;
31160 Roo.namespace("Roo.bootstrap.layout");/*
31162 * Ext JS Library 1.1.1
31163 * Copyright(c) 2006-2007, Ext JS, LLC.
31165 * Originally Released Under LGPL - original licence link has changed is not relivant.
31168 * <script type="text/javascript">
31172 * @class Roo.bootstrap.layout.Manager
31173 * @extends Roo.bootstrap.Component
31174 * Base class for layout managers.
31176 Roo.bootstrap.layout.Manager = function(config)
31178 Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31184 /** false to disable window resize monitoring @type Boolean */
31185 this.monitorWindowResize = true;
31190 * Fires when a layout is performed.
31191 * @param {Roo.LayoutManager} this
31195 * @event regionresized
31196 * Fires when the user resizes a region.
31197 * @param {Roo.LayoutRegion} region The resized region
31198 * @param {Number} newSize The new size (width for east/west, height for north/south)
31200 "regionresized" : true,
31202 * @event regioncollapsed
31203 * Fires when a region is collapsed.
31204 * @param {Roo.LayoutRegion} region The collapsed region
31206 "regioncollapsed" : true,
31208 * @event regionexpanded
31209 * Fires when a region is expanded.
31210 * @param {Roo.LayoutRegion} region The expanded region
31212 "regionexpanded" : true
31214 this.updating = false;
31217 this.el = Roo.get(config.el);
31223 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31228 monitorWindowResize : true,
31234 onRender : function(ct, position)
31237 this.el = Roo.get(ct);
31243 initEvents: function()
31247 // ie scrollbar fix
31248 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31249 document.body.scroll = "no";
31250 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31251 this.el.position('relative');
31253 this.id = this.el.id;
31254 this.el.addClass("roo-layout-container");
31255 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31256 if(this.el.dom != document.body ) {
31257 this.el.on('resize', this.layout,this);
31258 this.el.on('show', this.layout,this);
31264 * Returns true if this layout is currently being updated
31265 * @return {Boolean}
31267 isUpdating : function(){
31268 return this.updating;
31272 * Suspend the LayoutManager from doing auto-layouts while
31273 * making multiple add or remove calls
31275 beginUpdate : function(){
31276 this.updating = true;
31280 * Restore auto-layouts and optionally disable the manager from performing a layout
31281 * @param {Boolean} noLayout true to disable a layout update
31283 endUpdate : function(noLayout){
31284 this.updating = false;
31290 layout: function(){
31294 onRegionResized : function(region, newSize){
31295 this.fireEvent("regionresized", region, newSize);
31299 onRegionCollapsed : function(region){
31300 this.fireEvent("regioncollapsed", region);
31303 onRegionExpanded : function(region){
31304 this.fireEvent("regionexpanded", region);
31308 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31309 * performs box-model adjustments.
31310 * @return {Object} The size as an object {width: (the width), height: (the height)}
31312 getViewSize : function()
31315 if(this.el.dom != document.body){
31316 size = this.el.getSize();
31318 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31320 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31321 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31326 * Returns the Element this layout is bound to.
31327 * @return {Roo.Element}
31329 getEl : function(){
31334 * Returns the specified region.
31335 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31336 * @return {Roo.LayoutRegion}
31338 getRegion : function(target){
31339 return this.regions[target.toLowerCase()];
31342 onWindowResize : function(){
31343 if(this.monitorWindowResize){
31349 * Ext JS Library 1.1.1
31350 * Copyright(c) 2006-2007, Ext JS, LLC.
31352 * Originally Released Under LGPL - original licence link has changed is not relivant.
31355 * <script type="text/javascript">
31358 * @class Roo.bootstrap.layout.Border
31359 * @extends Roo.bootstrap.layout.Manager
31360 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31361 * please see: examples/bootstrap/nested.html<br><br>
31363 <b>The container the layout is rendered into can be either the body element or any other element.
31364 If it is not the body element, the container needs to either be an absolute positioned element,
31365 or you will need to add "position:relative" to the css of the container. You will also need to specify
31366 the container size if it is not the body element.</b>
31369 * Create a new Border
31370 * @param {Object} config Configuration options
31372 Roo.bootstrap.layout.Border = function(config){
31373 config = config || {};
31374 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31378 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31379 if(config[region]){
31380 config[region].region = region;
31381 this.addRegion(config[region]);
31387 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
31389 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31391 * Creates and adds a new region if it doesn't already exist.
31392 * @param {String} target The target region key (north, south, east, west or center).
31393 * @param {Object} config The regions config object
31394 * @return {BorderLayoutRegion} The new region
31396 addRegion : function(config)
31398 if(!this.regions[config.region]){
31399 var r = this.factory(config);
31400 this.bindRegion(r);
31402 return this.regions[config.region];
31406 bindRegion : function(r){
31407 this.regions[r.config.region] = r;
31409 r.on("visibilitychange", this.layout, this);
31410 r.on("paneladded", this.layout, this);
31411 r.on("panelremoved", this.layout, this);
31412 r.on("invalidated", this.layout, this);
31413 r.on("resized", this.onRegionResized, this);
31414 r.on("collapsed", this.onRegionCollapsed, this);
31415 r.on("expanded", this.onRegionExpanded, this);
31419 * Performs a layout update.
31421 layout : function()
31423 if(this.updating) {
31426 var size = this.getViewSize();
31427 var w = size.width;
31428 var h = size.height;
31433 //var x = 0, y = 0;
31435 var rs = this.regions;
31436 var north = rs["north"];
31437 var south = rs["south"];
31438 var west = rs["west"];
31439 var east = rs["east"];
31440 var center = rs["center"];
31441 //if(this.hideOnLayout){ // not supported anymore
31442 //c.el.setStyle("display", "none");
31444 if(north && north.isVisible()){
31445 var b = north.getBox();
31446 var m = north.getMargins();
31447 b.width = w - (m.left+m.right);
31450 centerY = b.height + b.y + m.bottom;
31451 centerH -= centerY;
31452 north.updateBox(this.safeBox(b));
31454 if(south && south.isVisible()){
31455 var b = south.getBox();
31456 var m = south.getMargins();
31457 b.width = w - (m.left+m.right);
31459 var totalHeight = (b.height + m.top + m.bottom);
31460 b.y = h - totalHeight + m.top;
31461 centerH -= totalHeight;
31462 south.updateBox(this.safeBox(b));
31464 if(west && west.isVisible()){
31465 var b = west.getBox();
31466 var m = west.getMargins();
31467 b.height = centerH - (m.top+m.bottom);
31469 b.y = centerY + m.top;
31470 var totalWidth = (b.width + m.left + m.right);
31471 centerX += totalWidth;
31472 centerW -= totalWidth;
31473 west.updateBox(this.safeBox(b));
31475 if(east && east.isVisible()){
31476 var b = east.getBox();
31477 var m = east.getMargins();
31478 b.height = centerH - (m.top+m.bottom);
31479 var totalWidth = (b.width + m.left + m.right);
31480 b.x = w - totalWidth + m.left;
31481 b.y = centerY + m.top;
31482 centerW -= totalWidth;
31483 east.updateBox(this.safeBox(b));
31486 var m = center.getMargins();
31488 x: centerX + m.left,
31489 y: centerY + m.top,
31490 width: centerW - (m.left+m.right),
31491 height: centerH - (m.top+m.bottom)
31493 //if(this.hideOnLayout){
31494 //center.el.setStyle("display", "block");
31496 center.updateBox(this.safeBox(centerBox));
31499 this.fireEvent("layout", this);
31503 safeBox : function(box){
31504 box.width = Math.max(0, box.width);
31505 box.height = Math.max(0, box.height);
31510 * Adds a ContentPanel (or subclass) to this layout.
31511 * @param {String} target The target region key (north, south, east, west or center).
31512 * @param {Roo.ContentPanel} panel The panel to add
31513 * @return {Roo.ContentPanel} The added panel
31515 add : function(target, panel){
31517 target = target.toLowerCase();
31518 return this.regions[target].add(panel);
31522 * Remove a ContentPanel (or subclass) to this layout.
31523 * @param {String} target The target region key (north, south, east, west or center).
31524 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31525 * @return {Roo.ContentPanel} The removed panel
31527 remove : function(target, panel){
31528 target = target.toLowerCase();
31529 return this.regions[target].remove(panel);
31533 * Searches all regions for a panel with the specified id
31534 * @param {String} panelId
31535 * @return {Roo.ContentPanel} The panel or null if it wasn't found
31537 findPanel : function(panelId){
31538 var rs = this.regions;
31539 for(var target in rs){
31540 if(typeof rs[target] != "function"){
31541 var p = rs[target].getPanel(panelId);
31551 * Searches all regions for a panel with the specified id and activates (shows) it.
31552 * @param {String/ContentPanel} panelId The panels id or the panel itself
31553 * @return {Roo.ContentPanel} The shown panel or null
31555 showPanel : function(panelId) {
31556 var rs = this.regions;
31557 for(var target in rs){
31558 var r = rs[target];
31559 if(typeof r != "function"){
31560 if(r.hasPanel(panelId)){
31561 return r.showPanel(panelId);
31569 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31570 * @param {Roo.state.Provider} provider (optional) An alternate state provider
31573 restoreState : function(provider){
31575 provider = Roo.state.Manager;
31577 var sm = new Roo.LayoutStateManager();
31578 sm.init(this, provider);
31584 * Adds a xtype elements to the layout.
31588 xtype : 'ContentPanel',
31595 xtype : 'NestedLayoutPanel',
31601 items : [ ... list of content panels or nested layout panels.. ]
31605 * @param {Object} cfg Xtype definition of item to add.
31607 addxtype : function(cfg)
31609 // basically accepts a pannel...
31610 // can accept a layout region..!?!?
31611 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31614 // theory? children can only be panels??
31616 //if (!cfg.xtype.match(/Panel$/)) {
31621 if (typeof(cfg.region) == 'undefined') {
31622 Roo.log("Failed to add Panel, region was not set");
31626 var region = cfg.region;
31632 xitems = cfg.items;
31639 case 'Content': // ContentPanel (el, cfg)
31640 case 'Scroll': // ContentPanel (el, cfg)
31642 cfg.autoCreate = true;
31643 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31645 // var el = this.el.createChild();
31646 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31649 this.add(region, ret);
31653 case 'TreePanel': // our new panel!
31654 cfg.el = this.el.createChild();
31655 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31656 this.add(region, ret);
31661 // create a new Layout (which is a Border Layout...
31663 var clayout = cfg.layout;
31664 clayout.el = this.el.createChild();
31665 clayout.items = clayout.items || [];
31669 // replace this exitems with the clayout ones..
31670 xitems = clayout.items;
31672 // force background off if it's in center...
31673 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31674 cfg.background = false;
31676 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
31679 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31680 //console.log('adding nested layout panel ' + cfg.toSource());
31681 this.add(region, ret);
31682 nb = {}; /// find first...
31687 // needs grid and region
31689 //var el = this.getRegion(region).el.createChild();
31691 *var el = this.el.createChild();
31692 // create the grid first...
31693 cfg.grid.container = el;
31694 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31697 if (region == 'center' && this.active ) {
31698 cfg.background = false;
31701 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31703 this.add(region, ret);
31705 if (cfg.background) {
31706 // render grid on panel activation (if panel background)
31707 ret.on('activate', function(gp) {
31708 if (!gp.grid.rendered) {
31709 // gp.grid.render(el);
31713 // cfg.grid.render(el);
31719 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31720 // it was the old xcomponent building that caused this before.
31721 // espeically if border is the top element in the tree.
31731 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31733 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31734 this.add(region, ret);
31738 throw "Can not add '" + cfg.xtype + "' to Border";
31744 this.beginUpdate();
31748 Roo.each(xitems, function(i) {
31749 region = nb && i.region ? i.region : false;
31751 var add = ret.addxtype(i);
31754 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31755 if (!i.background) {
31756 abn[region] = nb[region] ;
31763 // make the last non-background panel active..
31764 //if (nb) { Roo.log(abn); }
31767 for(var r in abn) {
31768 region = this.getRegion(r);
31770 // tried using nb[r], but it does not work..
31772 region.showPanel(abn[r]);
31783 factory : function(cfg)
31786 var validRegions = Roo.bootstrap.layout.Border.regions;
31788 var target = cfg.region;
31791 var r = Roo.bootstrap.layout;
31795 return new r.North(cfg);
31797 return new r.South(cfg);
31799 return new r.East(cfg);
31801 return new r.West(cfg);
31803 return new r.Center(cfg);
31805 throw 'Layout region "'+target+'" not supported.';
31812 * Ext JS Library 1.1.1
31813 * Copyright(c) 2006-2007, Ext JS, LLC.
31815 * Originally Released Under LGPL - original licence link has changed is not relivant.
31818 * <script type="text/javascript">
31822 * @class Roo.bootstrap.layout.Basic
31823 * @extends Roo.util.Observable
31824 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31825 * and does not have a titlebar, tabs or any other features. All it does is size and position
31826 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31827 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
31828 * @cfg {string} region the region that it inhabits..
31829 * @cfg {bool} skipConfig skip config?
31833 Roo.bootstrap.layout.Basic = function(config){
31835 this.mgr = config.mgr;
31837 this.position = config.region;
31839 var skipConfig = config.skipConfig;
31843 * @scope Roo.BasicLayoutRegion
31847 * @event beforeremove
31848 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31849 * @param {Roo.LayoutRegion} this
31850 * @param {Roo.ContentPanel} panel The panel
31851 * @param {Object} e The cancel event object
31853 "beforeremove" : true,
31855 * @event invalidated
31856 * Fires when the layout for this region is changed.
31857 * @param {Roo.LayoutRegion} this
31859 "invalidated" : true,
31861 * @event visibilitychange
31862 * Fires when this region is shown or hidden
31863 * @param {Roo.LayoutRegion} this
31864 * @param {Boolean} visibility true or false
31866 "visibilitychange" : true,
31868 * @event paneladded
31869 * Fires when a panel is added.
31870 * @param {Roo.LayoutRegion} this
31871 * @param {Roo.ContentPanel} panel The panel
31873 "paneladded" : true,
31875 * @event panelremoved
31876 * Fires when a panel is removed.
31877 * @param {Roo.LayoutRegion} this
31878 * @param {Roo.ContentPanel} panel The panel
31880 "panelremoved" : true,
31882 * @event beforecollapse
31883 * Fires when this region before collapse.
31884 * @param {Roo.LayoutRegion} this
31886 "beforecollapse" : true,
31889 * Fires when this region is collapsed.
31890 * @param {Roo.LayoutRegion} this
31892 "collapsed" : true,
31895 * Fires when this region is expanded.
31896 * @param {Roo.LayoutRegion} this
31901 * Fires when this region is slid into view.
31902 * @param {Roo.LayoutRegion} this
31904 "slideshow" : true,
31907 * Fires when this region slides out of view.
31908 * @param {Roo.LayoutRegion} this
31910 "slidehide" : true,
31912 * @event panelactivated
31913 * Fires when a panel is activated.
31914 * @param {Roo.LayoutRegion} this
31915 * @param {Roo.ContentPanel} panel The activated panel
31917 "panelactivated" : true,
31920 * Fires when the user resizes this region.
31921 * @param {Roo.LayoutRegion} this
31922 * @param {Number} newSize The new size (width for east/west, height for north/south)
31926 /** A collection of panels in this region. @type Roo.util.MixedCollection */
31927 this.panels = new Roo.util.MixedCollection();
31928 this.panels.getKey = this.getPanelId.createDelegate(this);
31930 this.activePanel = null;
31931 // ensure listeners are added...
31933 if (config.listeners || config.events) {
31934 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
31935 listeners : config.listeners || {},
31936 events : config.events || {}
31940 if(skipConfig !== true){
31941 this.applyConfig(config);
31945 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
31947 getPanelId : function(p){
31951 applyConfig : function(config){
31952 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31953 this.config = config;
31958 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
31959 * the width, for horizontal (north, south) the height.
31960 * @param {Number} newSize The new width or height
31962 resizeTo : function(newSize){
31963 var el = this.el ? this.el :
31964 (this.activePanel ? this.activePanel.getEl() : null);
31966 switch(this.position){
31969 el.setWidth(newSize);
31970 this.fireEvent("resized", this, newSize);
31974 el.setHeight(newSize);
31975 this.fireEvent("resized", this, newSize);
31981 getBox : function(){
31982 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31985 getMargins : function(){
31986 return this.margins;
31989 updateBox : function(box){
31991 var el = this.activePanel.getEl();
31992 el.dom.style.left = box.x + "px";
31993 el.dom.style.top = box.y + "px";
31994 this.activePanel.setSize(box.width, box.height);
31998 * Returns the container element for this region.
31999 * @return {Roo.Element}
32001 getEl : function(){
32002 return this.activePanel;
32006 * Returns true if this region is currently visible.
32007 * @return {Boolean}
32009 isVisible : function(){
32010 return this.activePanel ? true : false;
32013 setActivePanel : function(panel){
32014 panel = this.getPanel(panel);
32015 if(this.activePanel && this.activePanel != panel){
32016 this.activePanel.setActiveState(false);
32017 this.activePanel.getEl().setLeftTop(-10000,-10000);
32019 this.activePanel = panel;
32020 panel.setActiveState(true);
32022 panel.setSize(this.box.width, this.box.height);
32024 this.fireEvent("panelactivated", this, panel);
32025 this.fireEvent("invalidated");
32029 * Show the specified panel.
32030 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32031 * @return {Roo.ContentPanel} The shown panel or null
32033 showPanel : function(panel){
32034 panel = this.getPanel(panel);
32036 this.setActivePanel(panel);
32042 * Get the active panel for this region.
32043 * @return {Roo.ContentPanel} The active panel or null
32045 getActivePanel : function(){
32046 return this.activePanel;
32050 * Add the passed ContentPanel(s)
32051 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32052 * @return {Roo.ContentPanel} The panel added (if only one was added)
32054 add : function(panel){
32055 if(arguments.length > 1){
32056 for(var i = 0, len = arguments.length; i < len; i++) {
32057 this.add(arguments[i]);
32061 if(this.hasPanel(panel)){
32062 this.showPanel(panel);
32065 var el = panel.getEl();
32066 if(el.dom.parentNode != this.mgr.el.dom){
32067 this.mgr.el.dom.appendChild(el.dom);
32069 if(panel.setRegion){
32070 panel.setRegion(this);
32072 this.panels.add(panel);
32073 el.setStyle("position", "absolute");
32074 if(!panel.background){
32075 this.setActivePanel(panel);
32076 if(this.config.initialSize && this.panels.getCount()==1){
32077 this.resizeTo(this.config.initialSize);
32080 this.fireEvent("paneladded", this, panel);
32085 * Returns true if the panel is in this region.
32086 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32087 * @return {Boolean}
32089 hasPanel : function(panel){
32090 if(typeof panel == "object"){ // must be panel obj
32091 panel = panel.getId();
32093 return this.getPanel(panel) ? true : false;
32097 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32098 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32099 * @param {Boolean} preservePanel Overrides the config preservePanel option
32100 * @return {Roo.ContentPanel} The panel that was removed
32102 remove : function(panel, preservePanel){
32103 panel = this.getPanel(panel);
32108 this.fireEvent("beforeremove", this, panel, e);
32109 if(e.cancel === true){
32112 var panelId = panel.getId();
32113 this.panels.removeKey(panelId);
32118 * Returns the panel specified or null if it's not in this region.
32119 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32120 * @return {Roo.ContentPanel}
32122 getPanel : function(id){
32123 if(typeof id == "object"){ // must be panel obj
32126 return this.panels.get(id);
32130 * Returns this regions position (north/south/east/west/center).
32133 getPosition: function(){
32134 return this.position;
32138 * Ext JS Library 1.1.1
32139 * Copyright(c) 2006-2007, Ext JS, LLC.
32141 * Originally Released Under LGPL - original licence link has changed is not relivant.
32144 * <script type="text/javascript">
32148 * @class Roo.bootstrap.layout.Region
32149 * @extends Roo.bootstrap.layout.Basic
32150 * This class represents a region in a layout manager.
32152 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32153 * @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})
32154 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
32155 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
32156 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
32157 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
32158 * @cfg {String} title The title for the region (overrides panel titles)
32159 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
32160 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32161 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
32162 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32163 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
32164 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32165 * the space available, similar to FireFox 1.5 tabs (defaults to false)
32166 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
32167 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
32168 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
32170 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
32171 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
32172 * @cfg {Boolean} disableTabTips True to disable tab tooltips
32173 * @cfg {Number} width For East/West panels
32174 * @cfg {Number} height For North/South panels
32175 * @cfg {Boolean} split To show the splitter
32176 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
32178 * @cfg {string} cls Extra CSS classes to add to region
32180 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32181 * @cfg {string} region the region that it inhabits..
32184 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
32185 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
32187 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
32188 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
32189 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
32191 Roo.bootstrap.layout.Region = function(config)
32193 this.applyConfig(config);
32195 var mgr = config.mgr;
32196 var pos = config.region;
32197 config.skipConfig = true;
32198 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32201 this.onRender(mgr.el);
32204 this.visible = true;
32205 this.collapsed = false;
32208 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32210 position: '', // set by wrapper (eg. north/south etc..)
32212 createBody : function(){
32213 /** This region's body element
32214 * @type Roo.Element */
32215 this.bodyEl = this.el.createChild({
32217 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32221 onRender: function(ctr, pos)
32223 var dh = Roo.DomHelper;
32224 /** This region's container element
32225 * @type Roo.Element */
32226 this.el = dh.append(ctr.dom, {
32228 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32230 /** This region's title element
32231 * @type Roo.Element */
32233 this.titleEl = dh.append(this.el.dom,
32236 unselectable: "on",
32237 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32239 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
32240 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32243 this.titleEl.enableDisplayMode();
32244 /** This region's title text element
32245 * @type HTMLElement */
32246 this.titleTextEl = this.titleEl.dom.firstChild;
32247 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32249 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32250 this.closeBtn.enableDisplayMode();
32251 this.closeBtn.on("click", this.closeClicked, this);
32252 this.closeBtn.hide();
32254 this.createBody(this.config);
32255 if(this.config.hideWhenEmpty){
32257 this.on("paneladded", this.validateVisibility, this);
32258 this.on("panelremoved", this.validateVisibility, this);
32260 if(this.autoScroll){
32261 this.bodyEl.setStyle("overflow", "auto");
32263 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32265 //if(c.titlebar !== false){
32266 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32267 this.titleEl.hide();
32269 this.titleEl.show();
32270 if(this.config.title){
32271 this.titleTextEl.innerHTML = this.config.title;
32275 if(this.config.collapsed){
32276 this.collapse(true);
32278 if(this.config.hidden){
32283 applyConfig : function(c)
32286 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32287 var dh = Roo.DomHelper;
32288 if(c.titlebar !== false){
32289 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32290 this.collapseBtn.on("click", this.collapse, this);
32291 this.collapseBtn.enableDisplayMode();
32293 if(c.showPin === true || this.showPin){
32294 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32295 this.stickBtn.enableDisplayMode();
32296 this.stickBtn.on("click", this.expand, this);
32297 this.stickBtn.hide();
32302 /** This region's collapsed element
32303 * @type Roo.Element */
32306 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32307 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32310 if(c.floatable !== false){
32311 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32312 this.collapsedEl.on("click", this.collapseClick, this);
32315 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32316 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32317 id: "message", unselectable: "on", style:{"float":"left"}});
32318 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32320 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32321 this.expandBtn.on("click", this.expand, this);
32325 if(this.collapseBtn){
32326 this.collapseBtn.setVisible(c.collapsible == true);
32329 this.cmargins = c.cmargins || this.cmargins ||
32330 (this.position == "west" || this.position == "east" ?
32331 {top: 0, left: 2, right:2, bottom: 0} :
32332 {top: 2, left: 0, right:0, bottom: 2});
32334 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32337 this.bottomTabs = c.tabPosition != "top";
32339 this.autoScroll = c.autoScroll || false;
32344 this.duration = c.duration || .30;
32345 this.slideDuration = c.slideDuration || .45;
32350 * Returns true if this region is currently visible.
32351 * @return {Boolean}
32353 isVisible : function(){
32354 return this.visible;
32358 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32359 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
32361 //setCollapsedTitle : function(title){
32362 // title = title || " ";
32363 // if(this.collapsedTitleTextEl){
32364 // this.collapsedTitleTextEl.innerHTML = title;
32368 getBox : function(){
32370 // if(!this.collapsed){
32371 b = this.el.getBox(false, true);
32373 // b = this.collapsedEl.getBox(false, true);
32378 getMargins : function(){
32379 return this.margins;
32380 //return this.collapsed ? this.cmargins : this.margins;
32383 highlight : function(){
32384 this.el.addClass("x-layout-panel-dragover");
32387 unhighlight : function(){
32388 this.el.removeClass("x-layout-panel-dragover");
32391 updateBox : function(box)
32394 if(!this.collapsed){
32395 this.el.dom.style.left = box.x + "px";
32396 this.el.dom.style.top = box.y + "px";
32397 this.updateBody(box.width, box.height);
32399 this.collapsedEl.dom.style.left = box.x + "px";
32400 this.collapsedEl.dom.style.top = box.y + "px";
32401 this.collapsedEl.setSize(box.width, box.height);
32404 this.tabs.autoSizeTabs();
32408 updateBody : function(w, h)
32411 this.el.setWidth(w);
32412 w -= this.el.getBorderWidth("rl");
32413 if(this.config.adjustments){
32414 w += this.config.adjustments[0];
32418 this.el.setHeight(h);
32419 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32420 h -= this.el.getBorderWidth("tb");
32421 if(this.config.adjustments){
32422 h += this.config.adjustments[1];
32424 this.bodyEl.setHeight(h);
32426 h = this.tabs.syncHeight(h);
32429 if(this.panelSize){
32430 w = w !== null ? w : this.panelSize.width;
32431 h = h !== null ? h : this.panelSize.height;
32433 if(this.activePanel){
32434 var el = this.activePanel.getEl();
32435 w = w !== null ? w : el.getWidth();
32436 h = h !== null ? h : el.getHeight();
32437 this.panelSize = {width: w, height: h};
32438 this.activePanel.setSize(w, h);
32440 if(Roo.isIE && this.tabs){
32441 this.tabs.el.repaint();
32446 * Returns the container element for this region.
32447 * @return {Roo.Element}
32449 getEl : function(){
32454 * Hides this region.
32457 //if(!this.collapsed){
32458 this.el.dom.style.left = "-2000px";
32461 // this.collapsedEl.dom.style.left = "-2000px";
32462 // this.collapsedEl.hide();
32464 this.visible = false;
32465 this.fireEvent("visibilitychange", this, false);
32469 * Shows this region if it was previously hidden.
32472 //if(!this.collapsed){
32475 // this.collapsedEl.show();
32477 this.visible = true;
32478 this.fireEvent("visibilitychange", this, true);
32481 closeClicked : function(){
32482 if(this.activePanel){
32483 this.remove(this.activePanel);
32487 collapseClick : function(e){
32489 e.stopPropagation();
32492 e.stopPropagation();
32498 * Collapses this region.
32499 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32502 collapse : function(skipAnim, skipCheck = false){
32503 if(this.collapsed) {
32507 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32509 this.collapsed = true;
32511 this.split.el.hide();
32513 if(this.config.animate && skipAnim !== true){
32514 this.fireEvent("invalidated", this);
32515 this.animateCollapse();
32517 this.el.setLocation(-20000,-20000);
32519 this.collapsedEl.show();
32520 this.fireEvent("collapsed", this);
32521 this.fireEvent("invalidated", this);
32527 animateCollapse : function(){
32532 * Expands this region if it was previously collapsed.
32533 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32534 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32537 expand : function(e, skipAnim){
32539 e.stopPropagation();
32541 if(!this.collapsed || this.el.hasActiveFx()) {
32545 this.afterSlideIn();
32548 this.collapsed = false;
32549 if(this.config.animate && skipAnim !== true){
32550 this.animateExpand();
32554 this.split.el.show();
32556 this.collapsedEl.setLocation(-2000,-2000);
32557 this.collapsedEl.hide();
32558 this.fireEvent("invalidated", this);
32559 this.fireEvent("expanded", this);
32563 animateExpand : function(){
32567 initTabs : function()
32569 this.bodyEl.setStyle("overflow", "hidden");
32570 var ts = new Roo.bootstrap.panel.Tabs({
32571 el: this.bodyEl.dom,
32572 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32573 disableTooltips: this.config.disableTabTips,
32574 toolbar : this.config.toolbar
32577 if(this.config.hideTabs){
32578 ts.stripWrap.setDisplayed(false);
32581 ts.resizeTabs = this.config.resizeTabs === true;
32582 ts.minTabWidth = this.config.minTabWidth || 40;
32583 ts.maxTabWidth = this.config.maxTabWidth || 250;
32584 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32585 ts.monitorResize = false;
32586 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32587 ts.bodyEl.addClass('roo-layout-tabs-body');
32588 this.panels.each(this.initPanelAsTab, this);
32591 initPanelAsTab : function(panel){
32592 var ti = this.tabs.addTab(
32594 panel.getTitle(), null,
32595 this.config.closeOnTab && panel.isClosable()
32597 if(panel.tabTip !== undefined){
32598 ti.setTooltip(panel.tabTip);
32600 ti.on("activate", function(){
32601 this.setActivePanel(panel);
32604 if(this.config.closeOnTab){
32605 ti.on("beforeclose", function(t, e){
32607 this.remove(panel);
32613 updatePanelTitle : function(panel, title)
32615 if(this.activePanel == panel){
32616 this.updateTitle(title);
32619 var ti = this.tabs.getTab(panel.getEl().id);
32621 if(panel.tabTip !== undefined){
32622 ti.setTooltip(panel.tabTip);
32627 updateTitle : function(title){
32628 if(this.titleTextEl && !this.config.title){
32629 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
32633 setActivePanel : function(panel)
32635 panel = this.getPanel(panel);
32636 if(this.activePanel && this.activePanel != panel){
32637 this.activePanel.setActiveState(false);
32639 this.activePanel = panel;
32640 panel.setActiveState(true);
32641 if(this.panelSize){
32642 panel.setSize(this.panelSize.width, this.panelSize.height);
32645 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32647 this.updateTitle(panel.getTitle());
32649 this.fireEvent("invalidated", this);
32651 this.fireEvent("panelactivated", this, panel);
32655 * Shows the specified panel.
32656 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32657 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32659 showPanel : function(panel)
32661 panel = this.getPanel(panel);
32664 var tab = this.tabs.getTab(panel.getEl().id);
32665 if(tab.isHidden()){
32666 this.tabs.unhideTab(tab.id);
32670 this.setActivePanel(panel);
32677 * Get the active panel for this region.
32678 * @return {Roo.ContentPanel} The active panel or null
32680 getActivePanel : function(){
32681 return this.activePanel;
32684 validateVisibility : function(){
32685 if(this.panels.getCount() < 1){
32686 this.updateTitle(" ");
32687 this.closeBtn.hide();
32690 if(!this.isVisible()){
32697 * Adds the passed ContentPanel(s) to this region.
32698 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32699 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32701 add : function(panel){
32702 if(arguments.length > 1){
32703 for(var i = 0, len = arguments.length; i < len; i++) {
32704 this.add(arguments[i]);
32708 if(this.hasPanel(panel)){
32709 this.showPanel(panel);
32712 panel.setRegion(this);
32713 this.panels.add(panel);
32714 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32715 this.bodyEl.dom.appendChild(panel.getEl().dom);
32716 if(panel.background !== true){
32717 this.setActivePanel(panel);
32719 this.fireEvent("paneladded", this, panel);
32725 this.initPanelAsTab(panel);
32729 if(panel.background !== true){
32730 this.tabs.activate(panel.getEl().id);
32732 this.fireEvent("paneladded", this, panel);
32737 * Hides the tab for the specified panel.
32738 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32740 hidePanel : function(panel){
32741 if(this.tabs && (panel = this.getPanel(panel))){
32742 this.tabs.hideTab(panel.getEl().id);
32747 * Unhides the tab for a previously hidden panel.
32748 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32750 unhidePanel : function(panel){
32751 if(this.tabs && (panel = this.getPanel(panel))){
32752 this.tabs.unhideTab(panel.getEl().id);
32756 clearPanels : function(){
32757 while(this.panels.getCount() > 0){
32758 this.remove(this.panels.first());
32763 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32764 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32765 * @param {Boolean} preservePanel Overrides the config preservePanel option
32766 * @return {Roo.ContentPanel} The panel that was removed
32768 remove : function(panel, preservePanel)
32770 panel = this.getPanel(panel);
32775 this.fireEvent("beforeremove", this, panel, e);
32776 if(e.cancel === true){
32779 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32780 var panelId = panel.getId();
32781 this.panels.removeKey(panelId);
32783 document.body.appendChild(panel.getEl().dom);
32786 this.tabs.removeTab(panel.getEl().id);
32787 }else if (!preservePanel){
32788 this.bodyEl.dom.removeChild(panel.getEl().dom);
32790 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32791 var p = this.panels.first();
32792 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32793 tempEl.appendChild(p.getEl().dom);
32794 this.bodyEl.update("");
32795 this.bodyEl.dom.appendChild(p.getEl().dom);
32797 this.updateTitle(p.getTitle());
32799 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32800 this.setActivePanel(p);
32802 panel.setRegion(null);
32803 if(this.activePanel == panel){
32804 this.activePanel = null;
32806 if(this.config.autoDestroy !== false && preservePanel !== true){
32807 try{panel.destroy();}catch(e){}
32809 this.fireEvent("panelremoved", this, panel);
32814 * Returns the TabPanel component used by this region
32815 * @return {Roo.TabPanel}
32817 getTabs : function(){
32821 createTool : function(parentEl, className){
32822 var btn = Roo.DomHelper.append(parentEl, {
32824 cls: "x-layout-tools-button",
32827 cls: "roo-layout-tools-button-inner " + className,
32831 btn.addClassOnOver("roo-layout-tools-button-over");
32836 * Ext JS Library 1.1.1
32837 * Copyright(c) 2006-2007, Ext JS, LLC.
32839 * Originally Released Under LGPL - original licence link has changed is not relivant.
32842 * <script type="text/javascript">
32848 * @class Roo.SplitLayoutRegion
32849 * @extends Roo.LayoutRegion
32850 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32852 Roo.bootstrap.layout.Split = function(config){
32853 this.cursor = config.cursor;
32854 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32857 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32859 splitTip : "Drag to resize.",
32860 collapsibleSplitTip : "Drag to resize. Double click to hide.",
32861 useSplitTips : false,
32863 applyConfig : function(config){
32864 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32867 onRender : function(ctr,pos) {
32869 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
32870 if(!this.config.split){
32875 var splitEl = Roo.DomHelper.append(ctr.dom, {
32877 id: this.el.id + "-split",
32878 cls: "roo-layout-split roo-layout-split-"+this.position,
32881 /** The SplitBar for this region
32882 * @type Roo.SplitBar */
32883 // does not exist yet...
32884 Roo.log([this.position, this.orientation]);
32886 this.split = new Roo.bootstrap.SplitBar({
32887 dragElement : splitEl,
32888 resizingElement: this.el,
32889 orientation : this.orientation
32892 this.split.on("moved", this.onSplitMove, this);
32893 this.split.useShim = this.config.useShim === true;
32894 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32895 if(this.useSplitTips){
32896 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32898 //if(config.collapsible){
32899 // this.split.el.on("dblclick", this.collapse, this);
32902 if(typeof this.config.minSize != "undefined"){
32903 this.split.minSize = this.config.minSize;
32905 if(typeof this.config.maxSize != "undefined"){
32906 this.split.maxSize = this.config.maxSize;
32908 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
32909 this.hideSplitter();
32914 getHMaxSize : function(){
32915 var cmax = this.config.maxSize || 10000;
32916 var center = this.mgr.getRegion("center");
32917 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32920 getVMaxSize : function(){
32921 var cmax = this.config.maxSize || 10000;
32922 var center = this.mgr.getRegion("center");
32923 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32926 onSplitMove : function(split, newSize){
32927 this.fireEvent("resized", this, newSize);
32931 * Returns the {@link Roo.SplitBar} for this region.
32932 * @return {Roo.SplitBar}
32934 getSplitBar : function(){
32939 this.hideSplitter();
32940 Roo.bootstrap.layout.Split.superclass.hide.call(this);
32943 hideSplitter : function(){
32945 this.split.el.setLocation(-2000,-2000);
32946 this.split.el.hide();
32952 this.split.el.show();
32954 Roo.bootstrap.layout.Split.superclass.show.call(this);
32957 beforeSlide: function(){
32958 if(Roo.isGecko){// firefox overflow auto bug workaround
32959 this.bodyEl.clip();
32961 this.tabs.bodyEl.clip();
32963 if(this.activePanel){
32964 this.activePanel.getEl().clip();
32966 if(this.activePanel.beforeSlide){
32967 this.activePanel.beforeSlide();
32973 afterSlide : function(){
32974 if(Roo.isGecko){// firefox overflow auto bug workaround
32975 this.bodyEl.unclip();
32977 this.tabs.bodyEl.unclip();
32979 if(this.activePanel){
32980 this.activePanel.getEl().unclip();
32981 if(this.activePanel.afterSlide){
32982 this.activePanel.afterSlide();
32988 initAutoHide : function(){
32989 if(this.autoHide !== false){
32990 if(!this.autoHideHd){
32991 var st = new Roo.util.DelayedTask(this.slideIn, this);
32992 this.autoHideHd = {
32993 "mouseout": function(e){
32994 if(!e.within(this.el, true)){
32998 "mouseover" : function(e){
33004 this.el.on(this.autoHideHd);
33008 clearAutoHide : function(){
33009 if(this.autoHide !== false){
33010 this.el.un("mouseout", this.autoHideHd.mouseout);
33011 this.el.un("mouseover", this.autoHideHd.mouseover);
33015 clearMonitor : function(){
33016 Roo.get(document).un("click", this.slideInIf, this);
33019 // these names are backwards but not changed for compat
33020 slideOut : function(){
33021 if(this.isSlid || this.el.hasActiveFx()){
33024 this.isSlid = true;
33025 if(this.collapseBtn){
33026 this.collapseBtn.hide();
33028 this.closeBtnState = this.closeBtn.getStyle('display');
33029 this.closeBtn.hide();
33031 this.stickBtn.show();
33034 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33035 this.beforeSlide();
33036 this.el.setStyle("z-index", 10001);
33037 this.el.slideIn(this.getSlideAnchor(), {
33038 callback: function(){
33040 this.initAutoHide();
33041 Roo.get(document).on("click", this.slideInIf, this);
33042 this.fireEvent("slideshow", this);
33049 afterSlideIn : function(){
33050 this.clearAutoHide();
33051 this.isSlid = false;
33052 this.clearMonitor();
33053 this.el.setStyle("z-index", "");
33054 if(this.collapseBtn){
33055 this.collapseBtn.show();
33057 this.closeBtn.setStyle('display', this.closeBtnState);
33059 this.stickBtn.hide();
33061 this.fireEvent("slidehide", this);
33064 slideIn : function(cb){
33065 if(!this.isSlid || this.el.hasActiveFx()){
33069 this.isSlid = false;
33070 this.beforeSlide();
33071 this.el.slideOut(this.getSlideAnchor(), {
33072 callback: function(){
33073 this.el.setLeftTop(-10000, -10000);
33075 this.afterSlideIn();
33083 slideInIf : function(e){
33084 if(!e.within(this.el)){
33089 animateCollapse : function(){
33090 this.beforeSlide();
33091 this.el.setStyle("z-index", 20000);
33092 var anchor = this.getSlideAnchor();
33093 this.el.slideOut(anchor, {
33094 callback : function(){
33095 this.el.setStyle("z-index", "");
33096 this.collapsedEl.slideIn(anchor, {duration:.3});
33098 this.el.setLocation(-10000,-10000);
33100 this.fireEvent("collapsed", this);
33107 animateExpand : function(){
33108 this.beforeSlide();
33109 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33110 this.el.setStyle("z-index", 20000);
33111 this.collapsedEl.hide({
33114 this.el.slideIn(this.getSlideAnchor(), {
33115 callback : function(){
33116 this.el.setStyle("z-index", "");
33119 this.split.el.show();
33121 this.fireEvent("invalidated", this);
33122 this.fireEvent("expanded", this);
33150 getAnchor : function(){
33151 return this.anchors[this.position];
33154 getCollapseAnchor : function(){
33155 return this.canchors[this.position];
33158 getSlideAnchor : function(){
33159 return this.sanchors[this.position];
33162 getAlignAdj : function(){
33163 var cm = this.cmargins;
33164 switch(this.position){
33180 getExpandAdj : function(){
33181 var c = this.collapsedEl, cm = this.cmargins;
33182 switch(this.position){
33184 return [-(cm.right+c.getWidth()+cm.left), 0];
33187 return [cm.right+c.getWidth()+cm.left, 0];
33190 return [0, -(cm.top+cm.bottom+c.getHeight())];
33193 return [0, cm.top+cm.bottom+c.getHeight()];
33199 * Ext JS Library 1.1.1
33200 * Copyright(c) 2006-2007, Ext JS, LLC.
33202 * Originally Released Under LGPL - original licence link has changed is not relivant.
33205 * <script type="text/javascript">
33208 * These classes are private internal classes
33210 Roo.bootstrap.layout.Center = function(config){
33211 config.region = "center";
33212 Roo.bootstrap.layout.Region.call(this, config);
33213 this.visible = true;
33214 this.minWidth = config.minWidth || 20;
33215 this.minHeight = config.minHeight || 20;
33218 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33220 // center panel can't be hidden
33224 // center panel can't be hidden
33227 getMinWidth: function(){
33228 return this.minWidth;
33231 getMinHeight: function(){
33232 return this.minHeight;
33245 Roo.bootstrap.layout.North = function(config)
33247 config.region = 'north';
33248 config.cursor = 'n-resize';
33250 Roo.bootstrap.layout.Split.call(this, config);
33254 this.split.placement = Roo.bootstrap.SplitBar.TOP;
33255 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33256 this.split.el.addClass("roo-layout-split-v");
33258 var size = config.initialSize || config.height;
33259 if(typeof size != "undefined"){
33260 this.el.setHeight(size);
33263 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33265 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33269 getBox : function(){
33270 if(this.collapsed){
33271 return this.collapsedEl.getBox();
33273 var box = this.el.getBox();
33275 box.height += this.split.el.getHeight();
33280 updateBox : function(box){
33281 if(this.split && !this.collapsed){
33282 box.height -= this.split.el.getHeight();
33283 this.split.el.setLeft(box.x);
33284 this.split.el.setTop(box.y+box.height);
33285 this.split.el.setWidth(box.width);
33287 if(this.collapsed){
33288 this.updateBody(box.width, null);
33290 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33298 Roo.bootstrap.layout.South = function(config){
33299 config.region = 'south';
33300 config.cursor = 's-resize';
33301 Roo.bootstrap.layout.Split.call(this, config);
33303 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33304 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33305 this.split.el.addClass("roo-layout-split-v");
33307 var size = config.initialSize || config.height;
33308 if(typeof size != "undefined"){
33309 this.el.setHeight(size);
33313 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33314 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33315 getBox : function(){
33316 if(this.collapsed){
33317 return this.collapsedEl.getBox();
33319 var box = this.el.getBox();
33321 var sh = this.split.el.getHeight();
33328 updateBox : function(box){
33329 if(this.split && !this.collapsed){
33330 var sh = this.split.el.getHeight();
33333 this.split.el.setLeft(box.x);
33334 this.split.el.setTop(box.y-sh);
33335 this.split.el.setWidth(box.width);
33337 if(this.collapsed){
33338 this.updateBody(box.width, null);
33340 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33344 Roo.bootstrap.layout.East = function(config){
33345 config.region = "east";
33346 config.cursor = "e-resize";
33347 Roo.bootstrap.layout.Split.call(this, config);
33349 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33350 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33351 this.split.el.addClass("roo-layout-split-h");
33353 var size = config.initialSize || config.width;
33354 if(typeof size != "undefined"){
33355 this.el.setWidth(size);
33358 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33359 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33360 getBox : function(){
33361 if(this.collapsed){
33362 return this.collapsedEl.getBox();
33364 var box = this.el.getBox();
33366 var sw = this.split.el.getWidth();
33373 updateBox : function(box){
33374 if(this.split && !this.collapsed){
33375 var sw = this.split.el.getWidth();
33377 this.split.el.setLeft(box.x);
33378 this.split.el.setTop(box.y);
33379 this.split.el.setHeight(box.height);
33382 if(this.collapsed){
33383 this.updateBody(null, box.height);
33385 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33389 Roo.bootstrap.layout.West = function(config){
33390 config.region = "west";
33391 config.cursor = "w-resize";
33393 Roo.bootstrap.layout.Split.call(this, config);
33395 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33396 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33397 this.split.el.addClass("roo-layout-split-h");
33401 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33402 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33404 onRender: function(ctr, pos)
33406 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33407 var size = this.config.initialSize || this.config.width;
33408 if(typeof size != "undefined"){
33409 this.el.setWidth(size);
33413 getBox : function(){
33414 if(this.collapsed){
33415 return this.collapsedEl.getBox();
33417 var box = this.el.getBox();
33419 box.width += this.split.el.getWidth();
33424 updateBox : function(box){
33425 if(this.split && !this.collapsed){
33426 var sw = this.split.el.getWidth();
33428 this.split.el.setLeft(box.x+box.width);
33429 this.split.el.setTop(box.y);
33430 this.split.el.setHeight(box.height);
33432 if(this.collapsed){
33433 this.updateBody(null, box.height);
33435 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33438 Roo.namespace("Roo.bootstrap.panel");/*
33440 * Ext JS Library 1.1.1
33441 * Copyright(c) 2006-2007, Ext JS, LLC.
33443 * Originally Released Under LGPL - original licence link has changed is not relivant.
33446 * <script type="text/javascript">
33449 * @class Roo.ContentPanel
33450 * @extends Roo.util.Observable
33451 * A basic ContentPanel element.
33452 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
33453 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
33454 * @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
33455 * @cfg {Boolean} closable True if the panel can be closed/removed
33456 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
33457 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33458 * @cfg {Toolbar} toolbar A toolbar for this panel
33459 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
33460 * @cfg {String} title The title for this panel
33461 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33462 * @cfg {String} url Calls {@link #setUrl} with this value
33463 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33464 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
33465 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
33466 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
33469 * Create a new ContentPanel.
33470 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33471 * @param {String/Object} config A string to set only the title or a config object
33472 * @param {String} content (optional) Set the HTML content for this panel
33473 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33475 Roo.bootstrap.panel.Content = function( config){
33477 var el = config.el;
33478 var content = config.content;
33480 if(config.autoCreate){ // xtype is available if this is called from factory
33483 this.el = Roo.get(el);
33484 if(!this.el && config && config.autoCreate){
33485 if(typeof config.autoCreate == "object"){
33486 if(!config.autoCreate.id){
33487 config.autoCreate.id = config.id||el;
33489 this.el = Roo.DomHelper.append(document.body,
33490 config.autoCreate, true);
33492 var elcfg = { tag: "div",
33493 cls: "roo-layout-inactive-content",
33497 elcfg.html = config.html;
33501 this.el = Roo.DomHelper.append(document.body, elcfg , true);
33504 this.closable = false;
33505 this.loaded = false;
33506 this.active = false;
33509 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33511 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33513 this.wrapEl = this.el.wrap();
33515 if (config.toolbar.items) {
33516 ti = config.toolbar.items ;
33517 delete config.toolbar.items ;
33521 this.toolbar.render(this.wrapEl, 'before');
33522 for(var i =0;i < ti.length;i++) {
33523 // Roo.log(['add child', items[i]]);
33524 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33526 this.toolbar.items = nitems;
33527 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33528 delete config.toolbar;
33532 // xtype created footer. - not sure if will work as we normally have to render first..
33533 if (this.footer && !this.footer.el && this.footer.xtype) {
33534 if (!this.wrapEl) {
33535 this.wrapEl = this.el.wrap();
33538 this.footer.container = this.wrapEl.createChild();
33540 this.footer = Roo.factory(this.footer, Roo);
33545 if(typeof config == "string"){
33546 this.title = config;
33548 Roo.apply(this, config);
33552 this.resizeEl = Roo.get(this.resizeEl, true);
33554 this.resizeEl = this.el;
33556 // handle view.xtype
33564 * Fires when this panel is activated.
33565 * @param {Roo.ContentPanel} this
33569 * @event deactivate
33570 * Fires when this panel is activated.
33571 * @param {Roo.ContentPanel} this
33573 "deactivate" : true,
33577 * Fires when this panel is resized if fitToFrame is true.
33578 * @param {Roo.ContentPanel} this
33579 * @param {Number} width The width after any component adjustments
33580 * @param {Number} height The height after any component adjustments
33586 * Fires when this tab is created
33587 * @param {Roo.ContentPanel} this
33598 if(this.autoScroll){
33599 this.resizeEl.setStyle("overflow", "auto");
33601 // fix randome scrolling
33602 //this.el.on('scroll', function() {
33603 // Roo.log('fix random scolling');
33604 // this.scrollTo('top',0);
33607 content = content || this.content;
33609 this.setContent(content);
33611 if(config && config.url){
33612 this.setUrl(this.url, this.params, this.loadOnce);
33617 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33619 if (this.view && typeof(this.view.xtype) != 'undefined') {
33620 this.view.el = this.el.appendChild(document.createElement("div"));
33621 this.view = Roo.factory(this.view);
33622 this.view.render && this.view.render(false, '');
33626 this.fireEvent('render', this);
33629 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33631 setRegion : function(region){
33632 this.region = region;
33634 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33636 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33641 * Returns the toolbar for this Panel if one was configured.
33642 * @return {Roo.Toolbar}
33644 getToolbar : function(){
33645 return this.toolbar;
33648 setActiveState : function(active){
33649 this.active = active;
33651 this.fireEvent("deactivate", this);
33653 this.fireEvent("activate", this);
33657 * Updates this panel's element
33658 * @param {String} content The new content
33659 * @param {Boolean} loadScripts (optional) true to look for and process scripts
33661 setContent : function(content, loadScripts){
33662 this.el.update(content, loadScripts);
33665 ignoreResize : function(w, h){
33666 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33669 this.lastSize = {width: w, height: h};
33674 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33675 * @return {Roo.UpdateManager} The UpdateManager
33677 getUpdateManager : function(){
33678 return this.el.getUpdateManager();
33681 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33682 * @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:
33685 url: "your-url.php",
33686 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33687 callback: yourFunction,
33688 scope: yourObject, //(optional scope)
33691 text: "Loading...",
33696 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33697 * 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.
33698 * @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}
33699 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33700 * @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.
33701 * @return {Roo.ContentPanel} this
33704 var um = this.el.getUpdateManager();
33705 um.update.apply(um, arguments);
33711 * 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.
33712 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33713 * @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)
33714 * @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)
33715 * @return {Roo.UpdateManager} The UpdateManager
33717 setUrl : function(url, params, loadOnce){
33718 if(this.refreshDelegate){
33719 this.removeListener("activate", this.refreshDelegate);
33721 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33722 this.on("activate", this.refreshDelegate);
33723 return this.el.getUpdateManager();
33726 _handleRefresh : function(url, params, loadOnce){
33727 if(!loadOnce || !this.loaded){
33728 var updater = this.el.getUpdateManager();
33729 updater.update(url, params, this._setLoaded.createDelegate(this));
33733 _setLoaded : function(){
33734 this.loaded = true;
33738 * Returns this panel's id
33741 getId : function(){
33746 * Returns this panel's element - used by regiosn to add.
33747 * @return {Roo.Element}
33749 getEl : function(){
33750 return this.wrapEl || this.el;
33755 adjustForComponents : function(width, height)
33757 //Roo.log('adjustForComponents ');
33758 if(this.resizeEl != this.el){
33759 width -= this.el.getFrameWidth('lr');
33760 height -= this.el.getFrameWidth('tb');
33763 var te = this.toolbar.getEl();
33764 height -= te.getHeight();
33765 te.setWidth(width);
33768 var te = this.footer.getEl();
33769 Roo.log("footer:" + te.getHeight());
33771 height -= te.getHeight();
33772 te.setWidth(width);
33776 if(this.adjustments){
33777 width += this.adjustments[0];
33778 height += this.adjustments[1];
33780 return {"width": width, "height": height};
33783 setSize : function(width, height){
33784 if(this.fitToFrame && !this.ignoreResize(width, height)){
33785 if(this.fitContainer && this.resizeEl != this.el){
33786 this.el.setSize(width, height);
33788 var size = this.adjustForComponents(width, height);
33789 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33790 this.fireEvent('resize', this, size.width, size.height);
33795 * Returns this panel's title
33798 getTitle : function(){
33803 * Set this panel's title
33804 * @param {String} title
33806 setTitle : function(title){
33807 this.title = title;
33809 this.region.updatePanelTitle(this, title);
33814 * Returns true is this panel was configured to be closable
33815 * @return {Boolean}
33817 isClosable : function(){
33818 return this.closable;
33821 beforeSlide : function(){
33823 this.resizeEl.clip();
33826 afterSlide : function(){
33828 this.resizeEl.unclip();
33832 * Force a content refresh from the URL specified in the {@link #setUrl} method.
33833 * Will fail silently if the {@link #setUrl} method has not been called.
33834 * This does not activate the panel, just updates its content.
33836 refresh : function(){
33837 if(this.refreshDelegate){
33838 this.loaded = false;
33839 this.refreshDelegate();
33844 * Destroys this panel
33846 destroy : function(){
33847 this.el.removeAllListeners();
33848 var tempEl = document.createElement("span");
33849 tempEl.appendChild(this.el.dom);
33850 tempEl.innerHTML = "";
33856 * form - if the content panel contains a form - this is a reference to it.
33857 * @type {Roo.form.Form}
33861 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33862 * This contains a reference to it.
33868 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33878 * @param {Object} cfg Xtype definition of item to add.
33882 getChildContainer: function () {
33883 return this.getEl();
33888 var ret = new Roo.factory(cfg);
33893 if (cfg.xtype.match(/^Form$/)) {
33896 //if (this.footer) {
33897 // el = this.footer.container.insertSibling(false, 'before');
33899 el = this.el.createChild();
33902 this.form = new Roo.form.Form(cfg);
33905 if ( this.form.allItems.length) {
33906 this.form.render(el.dom);
33910 // should only have one of theses..
33911 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33912 // views.. should not be just added - used named prop 'view''
33914 cfg.el = this.el.appendChild(document.createElement("div"));
33917 var ret = new Roo.factory(cfg);
33919 ret.render && ret.render(false, ''); // render blank..
33929 * @class Roo.bootstrap.panel.Grid
33930 * @extends Roo.bootstrap.panel.Content
33932 * Create a new GridPanel.
33933 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
33934 * @param {Object} config A the config object
33940 Roo.bootstrap.panel.Grid = function(config)
33944 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33945 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
33947 config.el = this.wrapper;
33948 //this.el = this.wrapper;
33950 if (config.container) {
33951 // ctor'ed from a Border/panel.grid
33954 this.wrapper.setStyle("overflow", "hidden");
33955 this.wrapper.addClass('roo-grid-container');
33960 if(config.toolbar){
33961 var tool_el = this.wrapper.createChild();
33962 this.toolbar = Roo.factory(config.toolbar);
33964 if (config.toolbar.items) {
33965 ti = config.toolbar.items ;
33966 delete config.toolbar.items ;
33970 this.toolbar.render(tool_el);
33971 for(var i =0;i < ti.length;i++) {
33972 // Roo.log(['add child', items[i]]);
33973 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33975 this.toolbar.items = nitems;
33977 delete config.toolbar;
33980 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
33981 config.grid.scrollBody = true;;
33982 config.grid.monitorWindowResize = false; // turn off autosizing
33983 config.grid.autoHeight = false;
33984 config.grid.autoWidth = false;
33986 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
33988 if (config.background) {
33989 // render grid on panel activation (if panel background)
33990 this.on('activate', function(gp) {
33991 if (!gp.grid.rendered) {
33992 gp.grid.render(el);
33993 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
33999 this.grid.render(this.wrapper);
34000 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34003 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34004 // ??? needed ??? config.el = this.wrapper;
34009 // xtype created footer. - not sure if will work as we normally have to render first..
34010 if (this.footer && !this.footer.el && this.footer.xtype) {
34012 var ctr = this.grid.getView().getFooterPanel(true);
34013 this.footer.dataSource = this.grid.dataSource;
34014 this.footer = Roo.factory(this.footer, Roo);
34015 this.footer.render(ctr);
34025 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34026 getId : function(){
34027 return this.grid.id;
34031 * Returns the grid for this panel
34032 * @return {Roo.bootstrap.Table}
34034 getGrid : function(){
34038 setSize : function(width, height){
34039 if(!this.ignoreResize(width, height)){
34040 var grid = this.grid;
34041 var size = this.adjustForComponents(width, height);
34042 var gridel = grid.getGridEl();
34043 gridel.setSize(size.width, size.height);
34045 var thd = grid.getGridEl().select('thead',true).first();
34046 var tbd = grid.getGridEl().select('tbody', true).first();
34048 tbd.setSize(width, height - thd.getHeight());
34057 beforeSlide : function(){
34058 this.grid.getView().scroller.clip();
34061 afterSlide : function(){
34062 this.grid.getView().scroller.unclip();
34065 destroy : function(){
34066 this.grid.destroy();
34068 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
34073 * @class Roo.bootstrap.panel.Nest
34074 * @extends Roo.bootstrap.panel.Content
34076 * Create a new Panel, that can contain a layout.Border.
34079 * @param {Roo.BorderLayout} layout The layout for this panel
34080 * @param {String/Object} config A string to set only the title or a config object
34082 Roo.bootstrap.panel.Nest = function(config)
34084 // construct with only one argument..
34085 /* FIXME - implement nicer consturctors
34086 if (layout.layout) {
34088 layout = config.layout;
34089 delete config.layout;
34091 if (layout.xtype && !layout.getEl) {
34092 // then layout needs constructing..
34093 layout = Roo.factory(layout, Roo);
34097 config.el = config.layout.getEl();
34099 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34101 config.layout.monitorWindowResize = false; // turn off autosizing
34102 this.layout = config.layout;
34103 this.layout.getEl().addClass("roo-layout-nested-layout");
34110 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34112 setSize : function(width, height){
34113 if(!this.ignoreResize(width, height)){
34114 var size = this.adjustForComponents(width, height);
34115 var el = this.layout.getEl();
34116 el.setSize(size.width, size.height);
34117 var touch = el.dom.offsetWidth;
34118 this.layout.layout();
34119 // ie requires a double layout on the first pass
34120 if(Roo.isIE && !this.initialized){
34121 this.initialized = true;
34122 this.layout.layout();
34127 // activate all subpanels if not currently active..
34129 setActiveState : function(active){
34130 this.active = active;
34132 this.fireEvent("deactivate", this);
34136 this.fireEvent("activate", this);
34137 // not sure if this should happen before or after..
34138 if (!this.layout) {
34139 return; // should not happen..
34142 for (var r in this.layout.regions) {
34143 reg = this.layout.getRegion(r);
34144 if (reg.getActivePanel()) {
34145 //reg.showPanel(reg.getActivePanel()); // force it to activate..
34146 reg.setActivePanel(reg.getActivePanel());
34149 if (!reg.panels.length) {
34152 reg.showPanel(reg.getPanel(0));
34161 * Returns the nested BorderLayout for this panel
34162 * @return {Roo.BorderLayout}
34164 getLayout : function(){
34165 return this.layout;
34169 * Adds a xtype elements to the layout of the nested panel
34173 xtype : 'ContentPanel',
34180 xtype : 'NestedLayoutPanel',
34186 items : [ ... list of content panels or nested layout panels.. ]
34190 * @param {Object} cfg Xtype definition of item to add.
34192 addxtype : function(cfg) {
34193 return this.layout.addxtype(cfg);
34198 * Ext JS Library 1.1.1
34199 * Copyright(c) 2006-2007, Ext JS, LLC.
34201 * Originally Released Under LGPL - original licence link has changed is not relivant.
34204 * <script type="text/javascript">
34207 * @class Roo.TabPanel
34208 * @extends Roo.util.Observable
34209 * A lightweight tab container.
34213 // basic tabs 1, built from existing content
34214 var tabs = new Roo.TabPanel("tabs1");
34215 tabs.addTab("script", "View Script");
34216 tabs.addTab("markup", "View Markup");
34217 tabs.activate("script");
34219 // more advanced tabs, built from javascript
34220 var jtabs = new Roo.TabPanel("jtabs");
34221 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34223 // set up the UpdateManager
34224 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34225 var updater = tab2.getUpdateManager();
34226 updater.setDefaultUrl("ajax1.htm");
34227 tab2.on('activate', updater.refresh, updater, true);
34229 // Use setUrl for Ajax loading
34230 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34231 tab3.setUrl("ajax2.htm", null, true);
34234 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34237 jtabs.activate("jtabs-1");
34240 * Create a new TabPanel.
34241 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34242 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34244 Roo.bootstrap.panel.Tabs = function(config){
34246 * The container element for this TabPanel.
34247 * @type Roo.Element
34249 this.el = Roo.get(config.el);
34252 if(typeof config == "boolean"){
34253 this.tabPosition = config ? "bottom" : "top";
34255 Roo.apply(this, config);
34259 if(this.tabPosition == "bottom"){
34260 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34261 this.el.addClass("roo-tabs-bottom");
34263 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34264 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34265 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34267 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34269 if(this.tabPosition != "bottom"){
34270 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34271 * @type Roo.Element
34273 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34274 this.el.addClass("roo-tabs-top");
34278 this.bodyEl.setStyle("position", "relative");
34280 this.active = null;
34281 this.activateDelegate = this.activate.createDelegate(this);
34286 * Fires when the active tab changes
34287 * @param {Roo.TabPanel} this
34288 * @param {Roo.TabPanelItem} activePanel The new active tab
34292 * @event beforetabchange
34293 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34294 * @param {Roo.TabPanel} this
34295 * @param {Object} e Set cancel to true on this object to cancel the tab change
34296 * @param {Roo.TabPanelItem} tab The tab being changed to
34298 "beforetabchange" : true
34301 Roo.EventManager.onWindowResize(this.onResize, this);
34302 this.cpad = this.el.getPadding("lr");
34303 this.hiddenCount = 0;
34306 // toolbar on the tabbar support...
34307 if (this.toolbar) {
34308 alert("no toolbar support yet");
34309 this.toolbar = false;
34311 var tcfg = this.toolbar;
34312 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
34313 this.toolbar = new Roo.Toolbar(tcfg);
34314 if (Roo.isSafari) {
34315 var tbl = tcfg.container.child('table', true);
34316 tbl.setAttribute('width', '100%');
34324 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34327 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34329 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34331 tabPosition : "top",
34333 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34335 currentTabWidth : 0,
34337 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34341 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34345 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34347 preferredTabWidth : 175,
34349 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34351 resizeTabs : false,
34353 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34355 monitorResize : true,
34357 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
34362 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34363 * @param {String} id The id of the div to use <b>or create</b>
34364 * @param {String} text The text for the tab
34365 * @param {String} content (optional) Content to put in the TabPanelItem body
34366 * @param {Boolean} closable (optional) True to create a close icon on the tab
34367 * @return {Roo.TabPanelItem} The created TabPanelItem
34369 addTab : function(id, text, content, closable)
34371 var item = new Roo.bootstrap.panel.TabItem({
34375 closable : closable
34377 this.addTabItem(item);
34379 item.setContent(content);
34385 * Returns the {@link Roo.TabPanelItem} with the specified id/index
34386 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34387 * @return {Roo.TabPanelItem}
34389 getTab : function(id){
34390 return this.items[id];
34394 * Hides the {@link Roo.TabPanelItem} with the specified id/index
34395 * @param {String/Number} id The id or index of the TabPanelItem to hide.
34397 hideTab : function(id){
34398 var t = this.items[id];
34401 this.hiddenCount++;
34402 this.autoSizeTabs();
34407 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34408 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34410 unhideTab : function(id){
34411 var t = this.items[id];
34413 t.setHidden(false);
34414 this.hiddenCount--;
34415 this.autoSizeTabs();
34420 * Adds an existing {@link Roo.TabPanelItem}.
34421 * @param {Roo.TabPanelItem} item The TabPanelItem to add
34423 addTabItem : function(item){
34424 this.items[item.id] = item;
34425 this.items.push(item);
34426 // if(this.resizeTabs){
34427 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34428 // this.autoSizeTabs();
34430 // item.autoSize();
34435 * Removes a {@link Roo.TabPanelItem}.
34436 * @param {String/Number} id The id or index of the TabPanelItem to remove.
34438 removeTab : function(id){
34439 var items = this.items;
34440 var tab = items[id];
34441 if(!tab) { return; }
34442 var index = items.indexOf(tab);
34443 if(this.active == tab && items.length > 1){
34444 var newTab = this.getNextAvailable(index);
34449 this.stripEl.dom.removeChild(tab.pnode.dom);
34450 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34451 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34453 items.splice(index, 1);
34454 delete this.items[tab.id];
34455 tab.fireEvent("close", tab);
34456 tab.purgeListeners();
34457 this.autoSizeTabs();
34460 getNextAvailable : function(start){
34461 var items = this.items;
34463 // look for a next tab that will slide over to
34464 // replace the one being removed
34465 while(index < items.length){
34466 var item = items[++index];
34467 if(item && !item.isHidden()){
34471 // if one isn't found select the previous tab (on the left)
34474 var item = items[--index];
34475 if(item && !item.isHidden()){
34483 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34484 * @param {String/Number} id The id or index of the TabPanelItem to disable.
34486 disableTab : function(id){
34487 var tab = this.items[id];
34488 if(tab && this.active != tab){
34494 * Enables a {@link Roo.TabPanelItem} that is disabled.
34495 * @param {String/Number} id The id or index of the TabPanelItem to enable.
34497 enableTab : function(id){
34498 var tab = this.items[id];
34503 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34504 * @param {String/Number} id The id or index of the TabPanelItem to activate.
34505 * @return {Roo.TabPanelItem} The TabPanelItem.
34507 activate : function(id){
34508 var tab = this.items[id];
34512 if(tab == this.active || tab.disabled){
34516 this.fireEvent("beforetabchange", this, e, tab);
34517 if(e.cancel !== true && !tab.disabled){
34519 this.active.hide();
34521 this.active = this.items[id];
34522 this.active.show();
34523 this.fireEvent("tabchange", this, this.active);
34529 * Gets the active {@link Roo.TabPanelItem}.
34530 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34532 getActiveTab : function(){
34533 return this.active;
34537 * Updates the tab body element to fit the height of the container element
34538 * for overflow scrolling
34539 * @param {Number} targetHeight (optional) Override the starting height from the elements height
34541 syncHeight : function(targetHeight){
34542 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34543 var bm = this.bodyEl.getMargins();
34544 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34545 this.bodyEl.setHeight(newHeight);
34549 onResize : function(){
34550 if(this.monitorResize){
34551 this.autoSizeTabs();
34556 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34558 beginUpdate : function(){
34559 this.updating = true;
34563 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34565 endUpdate : function(){
34566 this.updating = false;
34567 this.autoSizeTabs();
34571 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34573 autoSizeTabs : function(){
34574 var count = this.items.length;
34575 var vcount = count - this.hiddenCount;
34576 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34579 var w = Math.max(this.el.getWidth() - this.cpad, 10);
34580 var availWidth = Math.floor(w / vcount);
34581 var b = this.stripBody;
34582 if(b.getWidth() > w){
34583 var tabs = this.items;
34584 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34585 if(availWidth < this.minTabWidth){
34586 /*if(!this.sleft){ // incomplete scrolling code
34587 this.createScrollButtons();
34590 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34593 if(this.currentTabWidth < this.preferredTabWidth){
34594 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34600 * Returns the number of tabs in this TabPanel.
34603 getCount : function(){
34604 return this.items.length;
34608 * Resizes all the tabs to the passed width
34609 * @param {Number} The new width
34611 setTabWidth : function(width){
34612 this.currentTabWidth = width;
34613 for(var i = 0, len = this.items.length; i < len; i++) {
34614 if(!this.items[i].isHidden()) {
34615 this.items[i].setWidth(width);
34621 * Destroys this TabPanel
34622 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34624 destroy : function(removeEl){
34625 Roo.EventManager.removeResizeListener(this.onResize, this);
34626 for(var i = 0, len = this.items.length; i < len; i++){
34627 this.items[i].purgeListeners();
34629 if(removeEl === true){
34630 this.el.update("");
34635 createStrip : function(container)
34637 var strip = document.createElement("nav");
34638 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34639 container.appendChild(strip);
34643 createStripList : function(strip)
34645 // div wrapper for retard IE
34646 // returns the "tr" element.
34647 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34648 //'<div class="x-tabs-strip-wrap">'+
34649 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34650 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34651 return strip.firstChild; //.firstChild.firstChild.firstChild;
34653 createBody : function(container)
34655 var body = document.createElement("div");
34656 Roo.id(body, "tab-body");
34657 //Roo.fly(body).addClass("x-tabs-body");
34658 Roo.fly(body).addClass("tab-content");
34659 container.appendChild(body);
34662 createItemBody :function(bodyEl, id){
34663 var body = Roo.getDom(id);
34665 body = document.createElement("div");
34668 //Roo.fly(body).addClass("x-tabs-item-body");
34669 Roo.fly(body).addClass("tab-pane");
34670 bodyEl.insertBefore(body, bodyEl.firstChild);
34674 createStripElements : function(stripEl, text, closable)
34676 var td = document.createElement("li"); // was td..
34677 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34678 //stripEl.appendChild(td);
34680 td.className = "x-tabs-closable";
34681 if(!this.closeTpl){
34682 this.closeTpl = new Roo.Template(
34683 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34684 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34685 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
34688 var el = this.closeTpl.overwrite(td, {"text": text});
34689 var close = el.getElementsByTagName("div")[0];
34690 var inner = el.getElementsByTagName("em")[0];
34691 return {"el": el, "close": close, "inner": inner};
34694 // not sure what this is..
34696 //this.tabTpl = new Roo.Template(
34697 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34698 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34700 this.tabTpl = new Roo.Template(
34702 '<span unselectable="on"' +
34703 (this.disableTooltips ? '' : ' title="{text}"') +
34704 ' >{text}</span></span></a>'
34708 var el = this.tabTpl.overwrite(td, {"text": text});
34709 var inner = el.getElementsByTagName("span")[0];
34710 return {"el": el, "inner": inner};
34718 * @class Roo.TabPanelItem
34719 * @extends Roo.util.Observable
34720 * Represents an individual item (tab plus body) in a TabPanel.
34721 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34722 * @param {String} id The id of this TabPanelItem
34723 * @param {String} text The text for the tab of this TabPanelItem
34724 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34726 Roo.bootstrap.panel.TabItem = function(config){
34728 * The {@link Roo.TabPanel} this TabPanelItem belongs to
34729 * @type Roo.TabPanel
34731 this.tabPanel = config.panel;
34733 * The id for this TabPanelItem
34736 this.id = config.id;
34738 this.disabled = false;
34740 this.text = config.text;
34742 this.loaded = false;
34743 this.closable = config.closable;
34746 * The body element for this TabPanelItem.
34747 * @type Roo.Element
34749 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34750 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34751 this.bodyEl.setStyle("display", "block");
34752 this.bodyEl.setStyle("zoom", "1");
34753 //this.hideAction();
34755 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34757 this.el = Roo.get(els.el);
34758 this.inner = Roo.get(els.inner, true);
34759 this.textEl = Roo.get(this.el.dom.firstChild, true);
34760 this.pnode = Roo.get(els.el.parentNode, true);
34761 this.el.on("mousedown", this.onTabMouseDown, this);
34762 this.el.on("click", this.onTabClick, this);
34764 if(config.closable){
34765 var c = Roo.get(els.close, true);
34766 c.dom.title = this.closeText;
34767 c.addClassOnOver("close-over");
34768 c.on("click", this.closeClick, this);
34774 * Fires when this tab becomes the active tab.
34775 * @param {Roo.TabPanel} tabPanel The parent TabPanel
34776 * @param {Roo.TabPanelItem} this
34780 * @event beforeclose
34781 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34782 * @param {Roo.TabPanelItem} this
34783 * @param {Object} e Set cancel to true on this object to cancel the close.
34785 "beforeclose": true,
34788 * Fires when this tab is closed.
34789 * @param {Roo.TabPanelItem} this
34793 * @event deactivate
34794 * Fires when this tab is no longer the active tab.
34795 * @param {Roo.TabPanel} tabPanel The parent TabPanel
34796 * @param {Roo.TabPanelItem} this
34798 "deactivate" : true
34800 this.hidden = false;
34802 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34805 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34807 purgeListeners : function(){
34808 Roo.util.Observable.prototype.purgeListeners.call(this);
34809 this.el.removeAllListeners();
34812 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34815 this.pnode.addClass("active");
34818 this.tabPanel.stripWrap.repaint();
34820 this.fireEvent("activate", this.tabPanel, this);
34824 * Returns true if this tab is the active tab.
34825 * @return {Boolean}
34827 isActive : function(){
34828 return this.tabPanel.getActiveTab() == this;
34832 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34835 this.pnode.removeClass("active");
34837 this.fireEvent("deactivate", this.tabPanel, this);
34840 hideAction : function(){
34841 this.bodyEl.hide();
34842 this.bodyEl.setStyle("position", "absolute");
34843 this.bodyEl.setLeft("-20000px");
34844 this.bodyEl.setTop("-20000px");
34847 showAction : function(){
34848 this.bodyEl.setStyle("position", "relative");
34849 this.bodyEl.setTop("");
34850 this.bodyEl.setLeft("");
34851 this.bodyEl.show();
34855 * Set the tooltip for the tab.
34856 * @param {String} tooltip The tab's tooltip
34858 setTooltip : function(text){
34859 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34860 this.textEl.dom.qtip = text;
34861 this.textEl.dom.removeAttribute('title');
34863 this.textEl.dom.title = text;
34867 onTabClick : function(e){
34868 e.preventDefault();
34869 this.tabPanel.activate(this.id);
34872 onTabMouseDown : function(e){
34873 e.preventDefault();
34874 this.tabPanel.activate(this.id);
34877 getWidth : function(){
34878 return this.inner.getWidth();
34881 setWidth : function(width){
34882 var iwidth = width - this.pnode.getPadding("lr");
34883 this.inner.setWidth(iwidth);
34884 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34885 this.pnode.setWidth(width);
34889 * Show or hide the tab
34890 * @param {Boolean} hidden True to hide or false to show.
34892 setHidden : function(hidden){
34893 this.hidden = hidden;
34894 this.pnode.setStyle("display", hidden ? "none" : "");
34898 * Returns true if this tab is "hidden"
34899 * @return {Boolean}
34901 isHidden : function(){
34902 return this.hidden;
34906 * Returns the text for this tab
34909 getText : function(){
34913 autoSize : function(){
34914 //this.el.beginMeasure();
34915 this.textEl.setWidth(1);
34917 * #2804 [new] Tabs in Roojs
34918 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34920 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34921 //this.el.endMeasure();
34925 * Sets the text for the tab (Note: this also sets the tooltip text)
34926 * @param {String} text The tab's text and tooltip
34928 setText : function(text){
34930 this.textEl.update(text);
34931 this.setTooltip(text);
34932 //if(!this.tabPanel.resizeTabs){
34933 // this.autoSize();
34937 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
34939 activate : function(){
34940 this.tabPanel.activate(this.id);
34944 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
34946 disable : function(){
34947 if(this.tabPanel.active != this){
34948 this.disabled = true;
34949 this.pnode.addClass("disabled");
34954 * Enables this TabPanelItem if it was previously disabled.
34956 enable : function(){
34957 this.disabled = false;
34958 this.pnode.removeClass("disabled");
34962 * Sets the content for this TabPanelItem.
34963 * @param {String} content The content
34964 * @param {Boolean} loadScripts true to look for and load scripts
34966 setContent : function(content, loadScripts){
34967 this.bodyEl.update(content, loadScripts);
34971 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
34972 * @return {Roo.UpdateManager} The UpdateManager
34974 getUpdateManager : function(){
34975 return this.bodyEl.getUpdateManager();
34979 * Set a URL to be used to load the content for this TabPanelItem.
34980 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
34981 * @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)
34982 * @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)
34983 * @return {Roo.UpdateManager} The UpdateManager
34985 setUrl : function(url, params, loadOnce){
34986 if(this.refreshDelegate){
34987 this.un('activate', this.refreshDelegate);
34989 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34990 this.on("activate", this.refreshDelegate);
34991 return this.bodyEl.getUpdateManager();
34995 _handleRefresh : function(url, params, loadOnce){
34996 if(!loadOnce || !this.loaded){
34997 var updater = this.bodyEl.getUpdateManager();
34998 updater.update(url, params, this._setLoaded.createDelegate(this));
35003 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
35004 * Will fail silently if the setUrl method has not been called.
35005 * This does not activate the panel, just updates its content.
35007 refresh : function(){
35008 if(this.refreshDelegate){
35009 this.loaded = false;
35010 this.refreshDelegate();
35015 _setLoaded : function(){
35016 this.loaded = true;
35020 closeClick : function(e){
35023 this.fireEvent("beforeclose", this, o);
35024 if(o.cancel !== true){
35025 this.tabPanel.removeTab(this.id);
35029 * The text displayed in the tooltip for the close icon.
35032 closeText : "Close this tab"