4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
107 // fill in the extra attributes
108 if (this.xattr && typeof(this.xattr) =='object') {
109 for (var i in this.xattr) {
110 cfg[i] = this.xattr[i];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
193 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195 return this.addxtypeChild(tree,cntr, is_body);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
234 addxtypeChild : function (tree, cntr, is_body)
236 Roo.debug && Roo.log('addxtypeChild:' + cntr);
238 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
241 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242 (typeof(tree['flexy:foreach']) != 'undefined');
246 skip_children = false;
247 // render the element if it's not BODY.
250 cn = Roo.factory(tree);
252 cn.parentType = this.xtype; //??
253 cn.parentId = this.id;
255 var build_from_html = Roo.XComponent.build_from_html;
258 // does the container contain child eleemnts with 'xtype' attributes.
259 // that match this xtype..
260 // note - when we render we create these as well..
261 // so we should check to see if body has xtype set.
262 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
264 var self_cntr_el = Roo.get(this[cntr](false));
265 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
267 //Roo.log(Roo.XComponent.build_from_html);
268 //Roo.log("got echild:");
271 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272 // and are not displayed -this causes this to use up the wrong element when matching.
273 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
276 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
283 //echild.dom.removeAttribute('xtype');
285 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286 Roo.debug && Roo.log(self_cntr_el);
287 Roo.debug && Roo.log(echild);
288 Roo.debug && Roo.log(cn);
294 // if object has flexy:if - then it may or may not be rendered.
295 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
296 // skip a flexy if element.
297 Roo.debug && Roo.log('skipping render');
298 Roo.debug && Roo.log(tree);
300 Roo.debug && Roo.log('skipping all children');
301 skip_children = true;
306 // actually if flexy:foreach is found, we really want to create
307 // multiple copies here...
309 //Roo.log(this[cntr]());
310 // some elements do not have render methods.. like the layouts...
311 cn.render && cn.render(this[cntr](true));
313 // then add the element..
321 if (typeof (tree.menu) != 'undefined') {
322 tree.menu.parentType = cn.xtype;
323 tree.menu.triggerEl = cn.el;
324 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
328 if (!tree.items || !tree.items.length) {
330 //Roo.log(["no children", this]);
335 var items = tree.items;
338 //Roo.log(items.length);
340 if (!skip_children) {
341 for(var i =0;i < items.length;i++) {
342 // Roo.log(['add child', items[i]]);
343 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
349 //Roo.log("fire childrenrendered");
351 cn.fireEvent('childrenrendered', this);
356 * Show a component - removes 'hidden' class
361 this.el.removeClass('hidden');
365 * Hide a component - adds 'hidden' class
369 if (this.el && !this.el.hasClass('hidden')) {
370 this.el.addClass('hidden');
384 * @class Roo.bootstrap.Body
385 * @extends Roo.bootstrap.Component
386 * Bootstrap Body class
390 * @param {Object} config The config object
393 Roo.bootstrap.Body = function(config){
394 Roo.bootstrap.Body.superclass.constructor.call(this, config);
395 this.el = Roo.get(document.body);
396 if (this.cls && this.cls.length) {
397 Roo.get(document.body).addClass(this.cls);
401 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
403 is_body : true,// just to make sure it's constructed?
408 onRender : function(ct, position)
410 /* Roo.log("Roo.bootstrap.Body - onRender");
411 if (this.cls && this.cls.length) {
412 Roo.get(document.body).addClass(this.cls);
432 * @class Roo.bootstrap.ButtonGroup
433 * @extends Roo.bootstrap.Component
434 * Bootstrap ButtonGroup class
435 * @cfg {String} size lg | sm | xs (default empty normal)
436 * @cfg {String} align vertical | justified (default none)
437 * @cfg {String} direction up | down (default down)
438 * @cfg {Boolean} toolbar false | true
439 * @cfg {Boolean} btn true | false
444 * @param {Object} config The config object
447 Roo.bootstrap.ButtonGroup = function(config){
448 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
459 getAutoCreate : function(){
465 cfg.html = this.html || cfg.html;
476 if (['vertical','justified'].indexOf(this.align)!==-1) {
477 cfg.cls = 'btn-group-' + this.align;
479 if (this.align == 'justified') {
480 console.log(this.items);
484 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
485 cfg.cls += ' btn-group-' + this.size;
488 if (this.direction == 'up') {
489 cfg.cls += ' dropup' ;
505 * @class Roo.bootstrap.Button
506 * @extends Roo.bootstrap.Component
507 * Bootstrap Button class
508 * @cfg {String} html The button content
509 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
510 * @cfg {String} size ( lg | sm | xs)
511 * @cfg {String} tag ( a | input | submit)
512 * @cfg {String} href empty or href
513 * @cfg {Boolean} disabled default false;
514 * @cfg {Boolean} isClose default false;
515 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
516 * @cfg {String} badge text for badge
517 * @cfg {String} theme default
518 * @cfg {Boolean} inverse
519 * @cfg {Boolean} toggle
520 * @cfg {String} ontext text for on toggle state
521 * @cfg {String} offtext text for off toggle state
522 * @cfg {Boolean} defaulton
523 * @cfg {Boolean} preventDefault default true
524 * @cfg {Boolean} removeClass remove the standard class..
525 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
528 * Create a new button
529 * @param {Object} config The config object
533 Roo.bootstrap.Button = function(config){
534 Roo.bootstrap.Button.superclass.constructor.call(this, config);
539 * When a butotn is pressed
540 * @param {Roo.bootstrap.Button} this
541 * @param {Roo.EventObject} e
546 * After the button has been toggles
547 * @param {Roo.EventObject} e
548 * @param {boolean} pressed (also available as button.pressed)
554 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
572 preventDefault: true,
581 getAutoCreate : function(){
589 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
590 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
595 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
597 if (this.toggle == true) {
600 cls: 'slider-frame roo-button',
605 'data-off-text':'OFF',
606 cls: 'slider-button',
612 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
613 cfg.cls += ' '+this.weight;
622 cfg["aria-hidden"] = true;
624 cfg.html = "×";
630 if (this.theme==='default') {
631 cfg.cls = 'btn roo-button';
633 //if (this.parentType != 'Navbar') {
634 this.weight = this.weight.length ? this.weight : 'default';
636 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
638 cfg.cls += ' btn-' + this.weight;
640 } else if (this.theme==='glow') {
643 cfg.cls = 'btn-glow roo-button';
645 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
647 cfg.cls += ' ' + this.weight;
653 this.cls += ' inverse';
658 cfg.cls += ' active';
662 cfg.disabled = 'disabled';
666 Roo.log('changing to ul' );
668 this.glyphicon = 'caret';
671 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
673 //gsRoo.log(this.parentType);
674 if (this.parentType === 'Navbar' && !this.parent().bar) {
675 Roo.log('changing to li?');
684 href : this.href || '#'
687 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
688 cfg.cls += ' dropdown';
695 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
697 if (this.glyphicon) {
698 cfg.html = ' ' + cfg.html;
703 cls: 'glyphicon glyphicon-' + this.glyphicon
713 // cfg.cls='btn roo-button';
717 var value = cfg.html;
722 cls: 'glyphicon glyphicon-' + this.glyphicon,
741 cfg.cls += ' dropdown';
742 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745 if (cfg.tag !== 'a' && this.href !== '') {
746 throw "Tag must be a to set href.";
747 } else if (this.href.length > 0) {
748 cfg.href = this.href;
751 if(this.removeClass){
756 cfg.target = this.target;
761 initEvents: function() {
762 // Roo.log('init events?');
763 // Roo.log(this.el.dom);
766 if (typeof (this.menu) != 'undefined') {
767 this.menu.parentType = this.xtype;
768 this.menu.triggerEl = this.el;
769 this.addxtype(Roo.apply({}, this.menu));
773 if (this.el.hasClass('roo-button')) {
774 this.el.on('click', this.onClick, this);
776 this.el.select('.roo-button').on('click', this.onClick, this);
779 if(this.removeClass){
780 this.el.on('click', this.onClick, this);
783 this.el.enableDisplayMode();
786 onClick : function(e)
793 Roo.log('button on click ');
794 if(this.preventDefault){
797 if (this.pressed === true || this.pressed === false) {
798 this.pressed = !this.pressed;
799 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
800 this.fireEvent('toggle', this, e, this.pressed);
804 this.fireEvent('click', this, e);
808 * Enables this button
812 this.disabled = false;
813 this.el.removeClass('disabled');
817 * Disable this button
821 this.disabled = true;
822 this.el.addClass('disabled');
825 * sets the active state on/off,
826 * @param {Boolean} state (optional) Force a particular state
828 setActive : function(v) {
830 this.el[v ? 'addClass' : 'removeClass']('active');
833 * toggles the current active state
835 toggleActive : function()
837 var active = this.el.hasClass('active');
838 this.setActive(!active);
842 setText : function(str)
844 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
848 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
871 * @class Roo.bootstrap.Column
872 * @extends Roo.bootstrap.Component
873 * Bootstrap Column class
874 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
875 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
876 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
877 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
878 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
879 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
880 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
881 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884 * @cfg {Boolean} hidden (true|false) hide the element
885 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
886 * @cfg {String} fa (ban|check|...) font awesome icon
887 * @cfg {Number} fasize (1|2|....) font awsome size
889 * @cfg {String} icon (info-sign|check|...) glyphicon name
891 * @cfg {String} html content of column.
894 * Create a new Column
895 * @param {Object} config The config object
898 Roo.bootstrap.Column = function(config){
899 Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
920 getAutoCreate : function(){
921 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
929 ['xs','sm','md','lg'].map(function(size){
930 //Roo.log( size + ':' + settings[size]);
932 if (settings[size+'off'] !== false) {
933 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936 if (settings[size] === false) {
940 if (!settings[size]) { // 0 = hidden
941 cfg.cls += ' hidden-' + size;
944 cfg.cls += ' col-' + size + '-' + settings[size];
949 cfg.cls += ' hidden';
952 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
953 cfg.cls +=' alert alert-' + this.alert;
957 if (this.html.length) {
958 cfg.html = this.html;
962 if (this.fasize > 1) {
963 fasize = ' fa-' + this.fasize + 'x';
965 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
970 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
989 * @class Roo.bootstrap.Container
990 * @extends Roo.bootstrap.Component
991 * Bootstrap Container class
992 * @cfg {Boolean} jumbotron is it a jumbotron element
993 * @cfg {String} html content of element
994 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
995 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
996 * @cfg {String} header content of header (for panel)
997 * @cfg {String} footer content of footer (for panel)
998 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
999 * @cfg {String} tag (header|aside|section) type of HTML tag.
1000 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1001 * @cfg {String} fa font awesome icon
1002 * @cfg {String} icon (info-sign|check|...) glyphicon name
1003 * @cfg {Boolean} hidden (true|false) hide the element
1004 * @cfg {Boolean} expandable (true|false) default false
1005 * @cfg {Boolean} expanded (true|false) default true
1006 * @cfg {String} rheader contet on the right of header
1007 * @cfg {Boolean} clickable (true|false) default false
1011 * Create a new Container
1012 * @param {Object} config The config object
1015 Roo.bootstrap.Container = function(config){
1016 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1022 * After the panel has been expand
1024 * @param {Roo.bootstrap.Container} this
1029 * After the panel has been collapsed
1031 * @param {Roo.bootstrap.Container} this
1036 * When a element is chick
1037 * @param {Roo.bootstrap.Container} this
1038 * @param {Roo.EventObject} e
1044 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1062 getChildContainer : function() {
1068 if (this.panel.length) {
1069 return this.el.select('.panel-body',true).first();
1076 getAutoCreate : function(){
1079 tag : this.tag || 'div',
1083 if (this.jumbotron) {
1084 cfg.cls = 'jumbotron';
1089 // - this is applied by the parent..
1091 // cfg.cls = this.cls + '';
1094 if (this.sticky.length) {
1096 var bd = Roo.get(document.body);
1097 if (!bd.hasClass('bootstrap-sticky')) {
1098 bd.addClass('bootstrap-sticky');
1099 Roo.select('html',true).setStyle('height', '100%');
1102 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1106 if (this.well.length) {
1107 switch (this.well) {
1110 cfg.cls +=' well well-' +this.well;
1119 cfg.cls += ' hidden';
1123 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1124 cfg.cls +=' alert alert-' + this.alert;
1129 if (this.panel.length) {
1130 cfg.cls += ' panel panel-' + this.panel;
1132 if (this.header.length) {
1136 if(this.expandable){
1138 cfg.cls = cfg.cls + ' expandable';
1142 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1150 cls : 'panel-title',
1151 html : (this.expandable ? ' ' : '') + this.header
1155 cls: 'panel-header-right',
1161 cls : 'panel-heading',
1162 style : this.expandable ? 'cursor: pointer' : '',
1170 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1175 if (this.footer.length) {
1177 cls : 'panel-footer',
1186 body.html = this.html || cfg.html;
1187 // prefix with the icons..
1189 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1197 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1198 cfg.cls = 'container';
1204 initEvents: function()
1206 if(this.expandable){
1207 var headerEl = this.headerEl();
1210 headerEl.on('click', this.onToggleClick, this);
1215 this.el.on('click', this.onClick, this);
1220 onToggleClick : function()
1222 var headerEl = this.headerEl();
1238 if(this.fireEvent('expand', this)) {
1240 this.expanded = true;
1242 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1244 this.el.select('.panel-body',true).first().removeClass('hide');
1246 var toggleEl = this.toggleEl();
1252 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1257 collapse : function()
1259 if(this.fireEvent('collapse', this)) {
1261 this.expanded = false;
1263 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1264 this.el.select('.panel-body',true).first().addClass('hide');
1266 var toggleEl = this.toggleEl();
1272 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1276 toggleEl : function()
1278 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1282 return this.el.select('.panel-heading .fa',true).first();
1285 headerEl : function()
1287 if(!this.el || !this.panel.length || !this.header.length){
1291 return this.el.select('.panel-heading',true).first()
1294 titleEl : function()
1296 if(!this.el || !this.panel.length || !this.header.length){
1300 return this.el.select('.panel-title',true).first();
1303 setTitle : function(v)
1305 var titleEl = this.titleEl();
1311 titleEl.dom.innerHTML = v;
1314 getTitle : function()
1317 var titleEl = this.titleEl();
1323 return titleEl.dom.innerHTML;
1326 setRightTitle : function(v)
1328 var t = this.el.select('.panel-header-right',true).first();
1334 t.dom.innerHTML = v;
1337 onClick : function(e)
1341 this.fireEvent('click', this, e);
1355 * @class Roo.bootstrap.Img
1356 * @extends Roo.bootstrap.Component
1357 * Bootstrap Img class
1358 * @cfg {Boolean} imgResponsive false | true
1359 * @cfg {String} border rounded | circle | thumbnail
1360 * @cfg {String} src image source
1361 * @cfg {String} alt image alternative text
1362 * @cfg {String} href a tag href
1363 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1364 * @cfg {String} xsUrl xs image source
1365 * @cfg {String} smUrl sm image source
1366 * @cfg {String} mdUrl md image source
1367 * @cfg {String} lgUrl lg image source
1370 * Create a new Input
1371 * @param {Object} config The config object
1374 Roo.bootstrap.Img = function(config){
1375 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1381 * The img click event for the img.
1382 * @param {Roo.EventObject} e
1388 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1390 imgResponsive: true,
1400 getAutoCreate : function()
1402 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1403 return this.createSingleImg();
1408 cls: 'roo-image-responsive-group',
1413 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1415 if(!_this[size + 'Url']){
1421 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1422 html: _this.html || cfg.html,
1423 src: _this[size + 'Url']
1426 img.cls += ' roo-image-responsive-' + size;
1428 var s = ['xs', 'sm', 'md', 'lg'];
1430 s.splice(s.indexOf(size), 1);
1432 Roo.each(s, function(ss){
1433 img.cls += ' hidden-' + ss;
1436 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1437 cfg.cls += ' img-' + _this.border;
1441 cfg.alt = _this.alt;
1454 a.target = _this.target;
1458 cfg.cn.push((_this.href) ? a : img);
1465 createSingleImg : function()
1469 cls: (this.imgResponsive) ? 'img-responsive' : '',
1471 src : 'about:blank' // just incase src get's set to undefined?!?
1474 cfg.html = this.html || cfg.html;
1476 cfg.src = this.src || cfg.src;
1478 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1479 cfg.cls += ' img-' + this.border;
1496 a.target = this.target;
1501 return (this.href) ? a : cfg;
1504 initEvents: function()
1507 this.el.on('click', this.onClick, this);
1512 onClick : function(e)
1514 Roo.log('img onclick');
1515 this.fireEvent('click', this, e);
1529 * @class Roo.bootstrap.Link
1530 * @extends Roo.bootstrap.Component
1531 * Bootstrap Link Class
1532 * @cfg {String} alt image alternative text
1533 * @cfg {String} href a tag href
1534 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1535 * @cfg {String} html the content of the link.
1536 * @cfg {String} anchor name for the anchor link
1537 * @cfg {String} fa - favicon
1539 * @cfg {Boolean} preventDefault (true | false) default false
1543 * Create a new Input
1544 * @param {Object} config The config object
1547 Roo.bootstrap.Link = function(config){
1548 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1554 * The img click event for the img.
1555 * @param {Roo.EventObject} e
1561 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1565 preventDefault: false,
1571 getAutoCreate : function()
1573 var html = this.html || '';
1575 if (this.fa !== false) {
1576 html = '<i class="fa fa-' + this.fa + '"></i>';
1581 // anchor's do not require html/href...
1582 if (this.anchor === false) {
1584 cfg.href = this.href || '#';
1586 cfg.name = this.anchor;
1587 if (this.html !== false || this.fa !== false) {
1590 if (this.href !== false) {
1591 cfg.href = this.href;
1595 if(this.alt !== false){
1600 if(this.target !== false) {
1601 cfg.target = this.target;
1607 initEvents: function() {
1609 if(!this.href || this.preventDefault){
1610 this.el.on('click', this.onClick, this);
1614 onClick : function(e)
1616 if(this.preventDefault){
1619 //Roo.log('img onclick');
1620 this.fireEvent('click', this, e);
1633 * @class Roo.bootstrap.Header
1634 * @extends Roo.bootstrap.Component
1635 * Bootstrap Header class
1636 * @cfg {String} html content of header
1637 * @cfg {Number} level (1|2|3|4|5|6) default 1
1640 * Create a new Header
1641 * @param {Object} config The config object
1645 Roo.bootstrap.Header = function(config){
1646 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1649 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1657 getAutoCreate : function(){
1662 tag: 'h' + (1 *this.level),
1663 html: this.html || ''
1675 * Ext JS Library 1.1.1
1676 * Copyright(c) 2006-2007, Ext JS, LLC.
1678 * Originally Released Under LGPL - original licence link has changed is not relivant.
1681 * <script type="text/javascript">
1685 * @class Roo.bootstrap.MenuMgr
1686 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1689 Roo.bootstrap.MenuMgr = function(){
1690 var menus, active, groups = {}, attached = false, lastShow = new Date();
1692 // private - called when first menu is created
1695 active = new Roo.util.MixedCollection();
1696 Roo.get(document).addKeyListener(27, function(){
1697 if(active.length > 0){
1705 if(active && active.length > 0){
1706 var c = active.clone();
1716 if(active.length < 1){
1717 Roo.get(document).un("mouseup", onMouseDown);
1725 var last = active.last();
1726 lastShow = new Date();
1729 Roo.get(document).on("mouseup", onMouseDown);
1734 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1735 m.parentMenu.activeChild = m;
1736 }else if(last && last.isVisible()){
1737 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1742 function onBeforeHide(m){
1744 m.activeChild.hide();
1746 if(m.autoHideTimer){
1747 clearTimeout(m.autoHideTimer);
1748 delete m.autoHideTimer;
1753 function onBeforeShow(m){
1754 var pm = m.parentMenu;
1755 if(!pm && !m.allowOtherMenus){
1757 }else if(pm && pm.activeChild && active != m){
1758 pm.activeChild.hide();
1762 // private this should really trigger on mouseup..
1763 function onMouseDown(e){
1764 Roo.log("on Mouse Up");
1766 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1767 Roo.log("MenuManager hideAll");
1776 function onBeforeCheck(mi, state){
1778 var g = groups[mi.group];
1779 for(var i = 0, l = g.length; i < l; i++){
1781 g[i].setChecked(false);
1790 * Hides all menus that are currently visible
1792 hideAll : function(){
1797 register : function(menu){
1801 menus[menu.id] = menu;
1802 menu.on("beforehide", onBeforeHide);
1803 menu.on("hide", onHide);
1804 menu.on("beforeshow", onBeforeShow);
1805 menu.on("show", onShow);
1807 if(g && menu.events["checkchange"]){
1811 groups[g].push(menu);
1812 menu.on("checkchange", onCheck);
1817 * Returns a {@link Roo.menu.Menu} object
1818 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1819 * be used to generate and return a new Menu instance.
1821 get : function(menu){
1822 if(typeof menu == "string"){ // menu id
1824 }else if(menu.events){ // menu instance
1827 /*else if(typeof menu.length == 'number'){ // array of menu items?
1828 return new Roo.bootstrap.Menu({items:menu});
1829 }else{ // otherwise, must be a config
1830 return new Roo.bootstrap.Menu(menu);
1837 unregister : function(menu){
1838 delete menus[menu.id];
1839 menu.un("beforehide", onBeforeHide);
1840 menu.un("hide", onHide);
1841 menu.un("beforeshow", onBeforeShow);
1842 menu.un("show", onShow);
1844 if(g && menu.events["checkchange"]){
1845 groups[g].remove(menu);
1846 menu.un("checkchange", onCheck);
1851 registerCheckable : function(menuItem){
1852 var g = menuItem.group;
1857 groups[g].push(menuItem);
1858 menuItem.on("beforecheckchange", onBeforeCheck);
1863 unregisterCheckable : function(menuItem){
1864 var g = menuItem.group;
1866 groups[g].remove(menuItem);
1867 menuItem.un("beforecheckchange", onBeforeCheck);
1879 * @class Roo.bootstrap.Menu
1880 * @extends Roo.bootstrap.Component
1881 * Bootstrap Menu class - container for MenuItems
1882 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1883 * @cfg {bool} hidden if the menu should be hidden when rendered.
1884 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1885 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1889 * @param {Object} config The config object
1893 Roo.bootstrap.Menu = function(config){
1894 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1895 if (this.registerMenu && this.type != 'treeview') {
1896 Roo.bootstrap.MenuMgr.register(this);
1901 * Fires before this menu is displayed
1902 * @param {Roo.menu.Menu} this
1907 * Fires before this menu is hidden
1908 * @param {Roo.menu.Menu} this
1913 * Fires after this menu is displayed
1914 * @param {Roo.menu.Menu} this
1919 * Fires after this menu is hidden
1920 * @param {Roo.menu.Menu} this
1925 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1926 * @param {Roo.menu.Menu} this
1927 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1928 * @param {Roo.EventObject} e
1933 * Fires when the mouse is hovering over this menu
1934 * @param {Roo.menu.Menu} this
1935 * @param {Roo.EventObject} e
1936 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1941 * Fires when the mouse exits this menu
1942 * @param {Roo.menu.Menu} this
1943 * @param {Roo.EventObject} e
1944 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1949 * Fires when a menu item contained in this menu is clicked
1950 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1951 * @param {Roo.EventObject} e
1955 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1958 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1962 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1965 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1967 registerMenu : true,
1969 menuItems :false, // stores the menu items..
1979 getChildContainer : function() {
1983 getAutoCreate : function(){
1985 //if (['right'].indexOf(this.align)!==-1) {
1986 // cfg.cn[1].cls += ' pull-right'
1992 cls : 'dropdown-menu' ,
1993 style : 'z-index:1000'
1997 if (this.type === 'submenu') {
1998 cfg.cls = 'submenu active';
2000 if (this.type === 'treeview') {
2001 cfg.cls = 'treeview-menu';
2006 initEvents : function() {
2008 // Roo.log("ADD event");
2009 // Roo.log(this.triggerEl.dom);
2011 this.triggerEl.on('click', this.onTriggerClick, this);
2013 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2015 this.triggerEl.addClass('dropdown-toggle');
2018 this.el.on('touchstart' , this.onTouch, this);
2020 this.el.on('click' , this.onClick, this);
2022 this.el.on("mouseover", this.onMouseOver, this);
2023 this.el.on("mouseout", this.onMouseOut, this);
2027 findTargetItem : function(e)
2029 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2033 //Roo.log(t); Roo.log(t.id);
2035 //Roo.log(this.menuitems);
2036 return this.menuitems.get(t.id);
2038 //return this.items.get(t.menuItemId);
2044 onTouch : function(e)
2046 Roo.log("menu.onTouch");
2047 //e.stopEvent(); this make the user popdown broken
2051 onClick : function(e)
2053 Roo.log("menu.onClick");
2055 var t = this.findTargetItem(e);
2056 if(!t || t.isContainer){
2061 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2062 if(t == this.activeItem && t.shouldDeactivate(e)){
2063 this.activeItem.deactivate();
2064 delete this.activeItem;
2068 this.setActiveItem(t, true);
2076 Roo.log('pass click event');
2080 this.fireEvent("click", this, t, e);
2084 (function() { _this.hide(); }).defer(500);
2087 onMouseOver : function(e){
2088 var t = this.findTargetItem(e);
2091 // if(t.canActivate && !t.disabled){
2092 // this.setActiveItem(t, true);
2096 this.fireEvent("mouseover", this, e, t);
2098 isVisible : function(){
2099 return !this.hidden;
2101 onMouseOut : function(e){
2102 var t = this.findTargetItem(e);
2105 // if(t == this.activeItem && t.shouldDeactivate(e)){
2106 // this.activeItem.deactivate();
2107 // delete this.activeItem;
2110 this.fireEvent("mouseout", this, e, t);
2115 * Displays this menu relative to another element
2116 * @param {String/HTMLElement/Roo.Element} element The element to align to
2117 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2118 * the element (defaults to this.defaultAlign)
2119 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2121 show : function(el, pos, parentMenu){
2122 this.parentMenu = parentMenu;
2126 this.fireEvent("beforeshow", this);
2127 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2130 * Displays this menu at a specific xy position
2131 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2132 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2134 showAt : function(xy, parentMenu, /* private: */_e){
2135 this.parentMenu = parentMenu;
2140 this.fireEvent("beforeshow", this);
2141 //xy = this.el.adjustForConstraints(xy);
2145 this.hideMenuItems();
2146 this.hidden = false;
2147 this.triggerEl.addClass('open');
2149 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2150 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2153 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2158 this.fireEvent("show", this);
2164 this.doFocus.defer(50, this);
2168 doFocus : function(){
2170 this.focusEl.focus();
2175 * Hides this menu and optionally all parent menus
2176 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2178 hide : function(deep)
2181 this.hideMenuItems();
2182 if(this.el && this.isVisible()){
2183 this.fireEvent("beforehide", this);
2184 if(this.activeItem){
2185 this.activeItem.deactivate();
2186 this.activeItem = null;
2188 this.triggerEl.removeClass('open');;
2190 this.fireEvent("hide", this);
2192 if(deep === true && this.parentMenu){
2193 this.parentMenu.hide(true);
2197 onTriggerClick : function(e)
2199 Roo.log('trigger click');
2201 var target = e.getTarget();
2203 Roo.log(target.nodeName.toLowerCase());
2205 if(target.nodeName.toLowerCase() === 'i'){
2211 onTriggerPress : function(e)
2213 Roo.log('trigger press');
2214 //Roo.log(e.getTarget());
2215 // Roo.log(this.triggerEl.dom);
2217 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2218 var pel = Roo.get(e.getTarget());
2219 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2220 Roo.log('is treeview or dropdown?');
2224 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2228 if (this.isVisible()) {
2233 this.show(this.triggerEl, false, false);
2236 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2243 hideMenuItems : function()
2245 Roo.log("hide Menu Items");
2249 //$(backdrop).remove()
2250 this.el.select('.open',true).each(function(aa) {
2252 aa.removeClass('open');
2253 //var parent = getParent($(this))
2254 //var relatedTarget = { relatedTarget: this }
2256 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2257 //if (e.isDefaultPrevented()) return
2258 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2261 addxtypeChild : function (tree, cntr) {
2262 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2264 this.menuitems.add(comp);
2285 * @class Roo.bootstrap.MenuItem
2286 * @extends Roo.bootstrap.Component
2287 * Bootstrap MenuItem class
2288 * @cfg {String} html the menu label
2289 * @cfg {String} href the link
2290 * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2291 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2292 * @cfg {Boolean} active used on sidebars to highlight active itesm
2293 * @cfg {String} fa favicon to show on left of menu item.
2294 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2298 * Create a new MenuItem
2299 * @param {Object} config The config object
2303 Roo.bootstrap.MenuItem = function(config){
2304 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2309 * The raw click event for the entire grid.
2310 * @param {Roo.bootstrap.MenuItem} this
2311 * @param {Roo.EventObject} e
2317 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2321 preventDefault: true,
2322 isContainer : false,
2326 getAutoCreate : function(){
2328 if(this.isContainer){
2331 cls: 'dropdown-menu-item'
2345 if (this.fa !== false) {
2348 cls : 'fa fa-' + this.fa
2357 cls: 'dropdown-menu-item',
2360 if (this.parent().type == 'treeview') {
2361 cfg.cls = 'treeview-menu';
2364 cfg.cls += ' active';
2369 anc.href = this.href || cfg.cn[0].href ;
2370 ctag.html = this.html || cfg.cn[0].html ;
2374 initEvents: function()
2376 if (this.parent().type == 'treeview') {
2377 this.el.select('a').on('click', this.onClick, this);
2380 this.menu.parentType = this.xtype;
2381 this.menu.triggerEl = this.el;
2382 this.menu = this.addxtype(Roo.apply({}, this.menu));
2386 onClick : function(e)
2388 Roo.log('item on click ');
2389 //if(this.preventDefault){
2390 // e.preventDefault();
2392 //this.parent().hideMenuItems();
2394 this.fireEvent('click', this, e);
2413 * @class Roo.bootstrap.MenuSeparator
2414 * @extends Roo.bootstrap.Component
2415 * Bootstrap MenuSeparator class
2418 * Create a new MenuItem
2419 * @param {Object} config The config object
2423 Roo.bootstrap.MenuSeparator = function(config){
2424 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2427 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2429 getAutoCreate : function(){
2448 * @class Roo.bootstrap.Modal
2449 * @extends Roo.bootstrap.Component
2450 * Bootstrap Modal class
2451 * @cfg {String} title Title of dialog
2452 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2453 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2454 * @cfg {Boolean} specificTitle default false
2455 * @cfg {Array} buttons Array of buttons or standard button set..
2456 * @cfg {String} buttonPosition (left|right|center) default right
2457 * @cfg {Boolean} animate default true
2458 * @cfg {Boolean} allow_close default true
2459 * @cfg {Boolean} fitwindow default true
2460 * @cfg {String} size (sm|lg) default empty
2464 * Create a new Modal Dialog
2465 * @param {Object} config The config object
2468 Roo.bootstrap.Modal = function(config){
2469 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2474 * The raw btnclick event for the button
2475 * @param {Roo.EventObject} e
2479 this.buttons = this.buttons || [];
2482 this.tmpl = Roo.factory(this.tmpl);
2487 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2489 title : 'test dialog',
2499 specificTitle: false,
2501 buttonPosition: 'right',
2520 onRender : function(ct, position)
2522 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2525 var cfg = Roo.apply({}, this.getAutoCreate());
2528 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2530 //if (!cfg.name.length) {
2534 cfg.cls += ' ' + this.cls;
2537 cfg.style = this.style;
2539 this.el = Roo.get(document.body).createChild(cfg, position);
2541 //var type = this.el.dom.type;
2544 if(this.tabIndex !== undefined){
2545 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2548 this.dialogEl = this.el.select('.modal-dialog',true).first();
2549 this.bodyEl = this.el.select('.modal-body',true).first();
2550 this.closeEl = this.el.select('.modal-header .close', true).first();
2551 this.footerEl = this.el.select('.modal-footer',true).first();
2552 this.titleEl = this.el.select('.modal-title',true).first();
2556 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2557 this.maskEl.enableDisplayMode("block");
2559 //this.el.addClass("x-dlg-modal");
2561 if (this.buttons.length) {
2562 Roo.each(this.buttons, function(bb) {
2563 var b = Roo.apply({}, bb);
2564 b.xns = b.xns || Roo.bootstrap;
2565 b.xtype = b.xtype || 'Button';
2566 if (typeof(b.listeners) == 'undefined') {
2567 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2570 var btn = Roo.factory(b);
2572 btn.render(this.el.select('.modal-footer div').first());
2576 // render the children.
2579 if(typeof(this.items) != 'undefined'){
2580 var items = this.items;
2583 for(var i =0;i < items.length;i++) {
2584 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2588 this.items = nitems;
2590 // where are these used - they used to be body/close/footer
2594 //this.el.addClass([this.fieldClass, this.cls]);
2598 getAutoCreate : function(){
2603 html : this.html || ''
2608 cls : 'modal-title',
2612 if(this.specificTitle){
2618 if (this.allow_close) {
2630 if(this.size.length){
2631 size = 'modal-' + this.size;
2636 style : 'display: none',
2639 cls: "modal-dialog " + size,
2642 cls : "modal-content",
2645 cls : 'modal-header',
2650 cls : 'modal-footer',
2654 cls: 'btn-' + this.buttonPosition
2671 modal.cls += ' fade';
2677 getChildContainer : function() {
2682 getButtonContainer : function() {
2683 return this.el.select('.modal-footer div',true).first();
2686 initEvents : function()
2688 if (this.allow_close) {
2689 this.closeEl.on('click', this.hide, this);
2691 Roo.EventManager.onWindowResize(this.resize, this, true);
2698 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2699 if (this.fitwindow) {
2700 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2701 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 30;
2706 setSize : function(w,h)
2716 if (!this.rendered) {
2720 this.el.setStyle('display', 'block');
2722 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2725 this.el.addClass('in');
2728 this.el.addClass('in');
2732 // not sure how we can show data in here..
2734 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2737 Roo.get(document.body).addClass("x-body-masked");
2738 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2740 this.el.setStyle('zIndex', '10001');
2742 this.fireEvent('show', this);
2743 this.items.forEach(function(e) {
2744 e.layout ? e.layout() : false;
2755 Roo.get(document.body).removeClass("x-body-masked");
2756 this.el.removeClass('in');
2757 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2759 if(this.animate){ // why
2761 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2763 this.el.setStyle('display', 'none');
2766 this.fireEvent('hide', this);
2769 addButton : function(str, cb)
2773 var b = Roo.apply({}, { html : str } );
2774 b.xns = b.xns || Roo.bootstrap;
2775 b.xtype = b.xtype || 'Button';
2776 if (typeof(b.listeners) == 'undefined') {
2777 b.listeners = { click : cb.createDelegate(this) };
2780 var btn = Roo.factory(b);
2782 btn.render(this.el.select('.modal-footer div').first());
2788 setDefaultButton : function(btn)
2790 //this.el.select('.modal-footer').()
2794 resizeTo: function(w,h)
2798 this.dialogEl.setWidth(w);
2799 if (this.diff === false) {
2800 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2803 this.bodyEl.setHeight(h-this.diff);
2807 setContentSize : function(w, h)
2811 onButtonClick: function(btn,e)
2814 this.fireEvent('btnclick', btn.name, e);
2817 * Set the title of the Dialog
2818 * @param {String} str new Title
2820 setTitle: function(str) {
2821 this.titleEl.dom.innerHTML = str;
2824 * Set the body of the Dialog
2825 * @param {String} str new Title
2827 setBody: function(str) {
2828 this.bodyEl.dom.innerHTML = str;
2831 * Set the body of the Dialog using the template
2832 * @param {Obj} data - apply this data to the template and replace the body contents.
2834 applyBody: function(obj)
2837 Roo.log("Error - using apply Body without a template");
2840 this.tmpl.overwrite(this.bodyEl, obj);
2846 Roo.apply(Roo.bootstrap.Modal, {
2848 * Button config that displays a single OK button
2857 * Button config that displays Yes and No buttons
2873 * Button config that displays OK and Cancel buttons
2888 * Button config that displays Yes, No and Cancel buttons
2911 * messagebox - can be used as a replace
2915 * @class Roo.MessageBox
2916 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2920 Roo.Msg.alert('Status', 'Changes saved successfully.');
2922 // Prompt for user data:
2923 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2925 // process text value...
2929 // Show a dialog using config options:
2931 title:'Save Changes?',
2932 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2933 buttons: Roo.Msg.YESNOCANCEL,
2940 Roo.bootstrap.MessageBox = function(){
2941 var dlg, opt, mask, waitTimer;
2942 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2943 var buttons, activeTextEl, bwidth;
2947 var handleButton = function(button){
2949 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2953 var handleHide = function(){
2955 dlg.el.removeClass(opt.cls);
2958 // Roo.TaskMgr.stop(waitTimer);
2959 // waitTimer = null;
2964 var updateButtons = function(b){
2967 buttons["ok"].hide();
2968 buttons["cancel"].hide();
2969 buttons["yes"].hide();
2970 buttons["no"].hide();
2971 //dlg.footer.dom.style.display = 'none';
2974 dlg.footerEl.dom.style.display = '';
2975 for(var k in buttons){
2976 if(typeof buttons[k] != "function"){
2979 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2980 width += buttons[k].el.getWidth()+15;
2990 var handleEsc = function(d, k, e){
2991 if(opt && opt.closable !== false){
3001 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3002 * @return {Roo.BasicDialog} The BasicDialog element
3004 getDialog : function(){
3006 dlg = new Roo.bootstrap.Modal( {
3009 //constraintoviewport:false,
3011 //collapsible : false,
3016 //buttonAlign:"center",
3017 closeClick : function(){
3018 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3021 handleButton("cancel");
3026 dlg.on("hide", handleHide);
3028 //dlg.addKeyListener(27, handleEsc);
3030 this.buttons = buttons;
3031 var bt = this.buttonText;
3032 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3033 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3034 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3035 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3037 bodyEl = dlg.bodyEl.createChild({
3039 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3040 '<textarea class="roo-mb-textarea"></textarea>' +
3041 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3043 msgEl = bodyEl.dom.firstChild;
3044 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3045 textboxEl.enableDisplayMode();
3046 textboxEl.addKeyListener([10,13], function(){
3047 if(dlg.isVisible() && opt && opt.buttons){
3050 }else if(opt.buttons.yes){
3051 handleButton("yes");
3055 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3056 textareaEl.enableDisplayMode();
3057 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3058 progressEl.enableDisplayMode();
3059 var pf = progressEl.dom.firstChild;
3061 pp = Roo.get(pf.firstChild);
3062 pp.setHeight(pf.offsetHeight);
3070 * Updates the message box body text
3071 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3072 * the XHTML-compliant non-breaking space character '&#160;')
3073 * @return {Roo.MessageBox} This message box
3075 updateText : function(text){
3076 if(!dlg.isVisible() && !opt.width){
3077 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3079 msgEl.innerHTML = text || ' ';
3081 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3082 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3084 Math.min(opt.width || cw , this.maxWidth),
3085 Math.max(opt.minWidth || this.minWidth, bwidth)
3088 activeTextEl.setWidth(w);
3090 if(dlg.isVisible()){
3091 dlg.fixedcenter = false;
3093 // to big, make it scroll. = But as usual stupid IE does not support
3096 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3097 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3098 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3100 bodyEl.dom.style.height = '';
3101 bodyEl.dom.style.overflowY = '';
3104 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3106 bodyEl.dom.style.overflowX = '';
3109 dlg.setContentSize(w, bodyEl.getHeight());
3110 if(dlg.isVisible()){
3111 dlg.fixedcenter = true;
3117 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3118 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3119 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3120 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3121 * @return {Roo.MessageBox} This message box
3123 updateProgress : function(value, text){
3125 this.updateText(text);
3127 if (pp) { // weird bug on my firefox - for some reason this is not defined
3128 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3134 * Returns true if the message box is currently displayed
3135 * @return {Boolean} True if the message box is visible, else false
3137 isVisible : function(){
3138 return dlg && dlg.isVisible();
3142 * Hides the message box if it is displayed
3145 if(this.isVisible()){
3151 * Displays a new message box, or reinitializes an existing message box, based on the config options
3152 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3153 * The following config object properties are supported:
3155 Property Type Description
3156 ---------- --------------- ------------------------------------------------------------------------------------
3157 animEl String/Element An id or Element from which the message box should animate as it opens and
3158 closes (defaults to undefined)
3159 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3160 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3161 closable Boolean False to hide the top-right close button (defaults to true). Note that
3162 progress and wait dialogs will ignore this property and always hide the
3163 close button as they can only be closed programmatically.
3164 cls String A custom CSS class to apply to the message box element
3165 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3166 displayed (defaults to 75)
3167 fn Function A callback function to execute after closing the dialog. The arguments to the
3168 function will be btn (the name of the button that was clicked, if applicable,
3169 e.g. "ok"), and text (the value of the active text field, if applicable).
3170 Progress and wait dialogs will ignore this option since they do not respond to
3171 user actions and can only be closed programmatically, so any required function
3172 should be called by the same code after it closes the dialog.
3173 icon String A CSS class that provides a background image to be used as an icon for
3174 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3175 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3176 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3177 modal Boolean False to allow user interaction with the page while the message box is
3178 displayed (defaults to true)
3179 msg String A string that will replace the existing message box body text (defaults
3180 to the XHTML-compliant non-breaking space character ' ')
3181 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3182 progress Boolean True to display a progress bar (defaults to false)
3183 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3184 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3185 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3186 title String The title text
3187 value String The string value to set into the active textbox element if displayed
3188 wait Boolean True to display a progress bar (defaults to false)
3189 width Number The width of the dialog in pixels
3196 msg: 'Please enter your address:',
3198 buttons: Roo.MessageBox.OKCANCEL,
3201 animEl: 'addAddressBtn'
3204 * @param {Object} config Configuration options
3205 * @return {Roo.MessageBox} This message box
3207 show : function(options)
3210 // this causes nightmares if you show one dialog after another
3211 // especially on callbacks..
3213 if(this.isVisible()){
3216 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3217 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3218 Roo.log("New Dialog Message:" + options.msg )
3219 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3220 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3223 var d = this.getDialog();
3225 d.setTitle(opt.title || " ");
3226 d.closeEl.setDisplayed(opt.closable !== false);
3227 activeTextEl = textboxEl;
3228 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3233 textareaEl.setHeight(typeof opt.multiline == "number" ?
3234 opt.multiline : this.defaultTextHeight);
3235 activeTextEl = textareaEl;
3244 progressEl.setDisplayed(opt.progress === true);
3245 this.updateProgress(0);
3246 activeTextEl.dom.value = opt.value || "";
3248 dlg.setDefaultButton(activeTextEl);
3250 var bs = opt.buttons;
3254 }else if(bs && bs.yes){
3255 db = buttons["yes"];
3257 dlg.setDefaultButton(db);
3259 bwidth = updateButtons(opt.buttons);
3260 this.updateText(opt.msg);
3262 d.el.addClass(opt.cls);
3264 d.proxyDrag = opt.proxyDrag === true;
3265 d.modal = opt.modal !== false;
3266 d.mask = opt.modal !== false ? mask : false;
3268 // force it to the end of the z-index stack so it gets a cursor in FF
3269 document.body.appendChild(dlg.el.dom);
3270 d.animateTarget = null;
3271 d.show(options.animEl);
3277 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3278 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3279 * and closing the message box when the process is complete.
3280 * @param {String} title The title bar text
3281 * @param {String} msg The message box body text
3282 * @return {Roo.MessageBox} This message box
3284 progress : function(title, msg){
3291 minWidth: this.minProgressWidth,
3298 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3299 * If a callback function is passed it will be called after the user clicks the button, and the
3300 * id of the button that was clicked will be passed as the only parameter to the callback
3301 * (could also be the top-right close button).
3302 * @param {String} title The title bar text
3303 * @param {String} msg The message box body text
3304 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3305 * @param {Object} scope (optional) The scope of the callback function
3306 * @return {Roo.MessageBox} This message box
3308 alert : function(title, msg, fn, scope){
3321 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3322 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3323 * You are responsible for closing the message box when the process is complete.
3324 * @param {String} msg The message box body text
3325 * @param {String} title (optional) The title bar text
3326 * @return {Roo.MessageBox} This message box
3328 wait : function(msg, title){
3339 waitTimer = Roo.TaskMgr.start({
3341 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3349 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3350 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3351 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3352 * @param {String} title The title bar text
3353 * @param {String} msg The message box body text
3354 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3355 * @param {Object} scope (optional) The scope of the callback function
3356 * @return {Roo.MessageBox} This message box
3358 confirm : function(title, msg, fn, scope){
3362 buttons: this.YESNO,
3371 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3372 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3373 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3374 * (could also be the top-right close button) and the text that was entered will be passed as the two
3375 * parameters to the callback.
3376 * @param {String} title The title bar text
3377 * @param {String} msg The message box body text
3378 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3379 * @param {Object} scope (optional) The scope of the callback function
3380 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3381 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3382 * @return {Roo.MessageBox} This message box
3384 prompt : function(title, msg, fn, scope, multiline){
3388 buttons: this.OKCANCEL,
3393 multiline: multiline,
3400 * Button config that displays a single OK button
3405 * Button config that displays Yes and No buttons
3408 YESNO : {yes:true, no:true},
3410 * Button config that displays OK and Cancel buttons
3413 OKCANCEL : {ok:true, cancel:true},
3415 * Button config that displays Yes, No and Cancel buttons
3418 YESNOCANCEL : {yes:true, no:true, cancel:true},
3421 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3424 defaultTextHeight : 75,
3426 * The maximum width in pixels of the message box (defaults to 600)
3431 * The minimum width in pixels of the message box (defaults to 100)
3436 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3437 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3440 minProgressWidth : 250,
3442 * An object containing the default button text strings that can be overriden for localized language support.
3443 * Supported properties are: ok, cancel, yes and no.
3444 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3457 * Shorthand for {@link Roo.MessageBox}
3459 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3460 Roo.Msg = Roo.Msg || Roo.MessageBox;
3469 * @class Roo.bootstrap.Navbar
3470 * @extends Roo.bootstrap.Component
3471 * Bootstrap Navbar class
3474 * Create a new Navbar
3475 * @param {Object} config The config object
3479 Roo.bootstrap.Navbar = function(config){
3480 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3484 * @event beforetoggle
3485 * Fire before toggle the menu
3486 * @param {Roo.EventObject} e
3488 "beforetoggle" : true
3492 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3501 getAutoCreate : function(){
3504 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3508 initEvents :function ()
3510 //Roo.log(this.el.select('.navbar-toggle',true));
3511 this.el.select('.navbar-toggle',true).on('click', function() {
3512 if(this.fireEvent('beforetoggle', this) !== false){
3513 this.el.select('.navbar-collapse',true).toggleClass('in');
3523 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3525 var size = this.el.getSize();
3526 this.maskEl.setSize(size.width, size.height);
3527 this.maskEl.enableDisplayMode("block");
3536 getChildContainer : function()
3538 if (this.el.select('.collapse').getCount()) {
3539 return this.el.select('.collapse',true).first();
3572 * @class Roo.bootstrap.NavSimplebar
3573 * @extends Roo.bootstrap.Navbar
3574 * Bootstrap Sidebar class
3576 * @cfg {Boolean} inverse is inverted color
3578 * @cfg {String} type (nav | pills | tabs)
3579 * @cfg {Boolean} arrangement stacked | justified
3580 * @cfg {String} align (left | right) alignment
3582 * @cfg {Boolean} main (true|false) main nav bar? default false
3583 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3585 * @cfg {String} tag (header|footer|nav|div) default is nav
3591 * Create a new Sidebar
3592 * @param {Object} config The config object
3596 Roo.bootstrap.NavSimplebar = function(config){
3597 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3600 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3616 getAutoCreate : function(){
3620 tag : this.tag || 'div',
3633 this.type = this.type || 'nav';
3634 if (['tabs','pills'].indexOf(this.type)!==-1) {
3635 cfg.cn[0].cls += ' nav-' + this.type
3639 if (this.type!=='nav') {
3640 Roo.log('nav type must be nav/tabs/pills')
3642 cfg.cn[0].cls += ' navbar-nav'
3648 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3649 cfg.cn[0].cls += ' nav-' + this.arrangement;
3653 if (this.align === 'right') {
3654 cfg.cn[0].cls += ' navbar-right';
3658 cfg.cls += ' navbar-inverse';
3685 * @class Roo.bootstrap.NavHeaderbar
3686 * @extends Roo.bootstrap.NavSimplebar
3687 * Bootstrap Sidebar class
3689 * @cfg {String} brand what is brand
3690 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3691 * @cfg {String} brand_href href of the brand
3692 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3693 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3694 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3695 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3698 * Create a new Sidebar
3699 * @param {Object} config The config object
3703 Roo.bootstrap.NavHeaderbar = function(config){
3704 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3708 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3715 desktopCenter : false,
3718 getAutoCreate : function(){
3721 tag: this.nav || 'nav',
3728 if (this.desktopCenter) {
3729 cn.push({cls : 'container', cn : []});
3736 cls: 'navbar-header',
3741 cls: 'navbar-toggle',
3742 'data-toggle': 'collapse',
3747 html: 'Toggle navigation'
3769 cls: 'collapse navbar-collapse',
3773 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3775 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3776 cfg.cls += ' navbar-' + this.position;
3778 // tag can override this..
3780 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3783 if (this.brand !== '') {
3786 href: this.brand_href ? this.brand_href : '#',
3787 cls: 'navbar-brand',
3795 cfg.cls += ' main-nav';
3803 getHeaderChildContainer : function()
3805 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3806 return this.el.select('.navbar-header',true).first();
3809 return this.getChildContainer();
3813 initEvents : function()
3815 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3817 if (this.autohide) {
3822 Roo.get(document).on('scroll',function(e) {
3823 var ns = Roo.get(document).getScroll().top;
3824 var os = prevScroll;
3828 ft.removeClass('slideDown');
3829 ft.addClass('slideUp');
3832 ft.removeClass('slideUp');
3833 ft.addClass('slideDown');
3854 * @class Roo.bootstrap.NavSidebar
3855 * @extends Roo.bootstrap.Navbar
3856 * Bootstrap Sidebar class
3859 * Create a new Sidebar
3860 * @param {Object} config The config object
3864 Roo.bootstrap.NavSidebar = function(config){
3865 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3868 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3870 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3872 getAutoCreate : function(){
3877 cls: 'sidebar sidebar-nav'
3899 * @class Roo.bootstrap.NavGroup
3900 * @extends Roo.bootstrap.Component
3901 * Bootstrap NavGroup class
3902 * @cfg {String} align (left|right)
3903 * @cfg {Boolean} inverse
3904 * @cfg {String} type (nav|pills|tab) default nav
3905 * @cfg {String} navId - reference Id for navbar.
3909 * Create a new nav group
3910 * @param {Object} config The config object
3913 Roo.bootstrap.NavGroup = function(config){
3914 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3917 Roo.bootstrap.NavGroup.register(this);
3921 * Fires when the active item changes
3922 * @param {Roo.bootstrap.NavGroup} this
3923 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3924 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3931 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3942 getAutoCreate : function()
3944 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3951 if (['tabs','pills'].indexOf(this.type)!==-1) {
3952 cfg.cls += ' nav-' + this.type
3954 if (this.type!=='nav') {
3955 Roo.log('nav type must be nav/tabs/pills')
3957 cfg.cls += ' navbar-nav'
3960 if (this.parent().sidebar) {
3963 cls: 'dashboard-menu sidebar-menu'
3969 if (this.form === true) {
3975 if (this.align === 'right') {
3976 cfg.cls += ' navbar-right';
3978 cfg.cls += ' navbar-left';
3982 if (this.align === 'right') {
3983 cfg.cls += ' navbar-right';
3987 cfg.cls += ' navbar-inverse';
3995 * sets the active Navigation item
3996 * @param {Roo.bootstrap.NavItem} the new current navitem
3998 setActiveItem : function(item)
4001 Roo.each(this.navItems, function(v){
4006 v.setActive(false, true);
4013 item.setActive(true, true);
4014 this.fireEvent('changed', this, item, prev);
4019 * gets the active Navigation item
4020 * @return {Roo.bootstrap.NavItem} the current navitem
4022 getActive : function()
4026 Roo.each(this.navItems, function(v){
4037 indexOfNav : function()
4041 Roo.each(this.navItems, function(v,i){
4052 * adds a Navigation item
4053 * @param {Roo.bootstrap.NavItem} the navitem to add
4055 addItem : function(cfg)
4057 var cn = new Roo.bootstrap.NavItem(cfg);
4059 cn.parentId = this.id;
4060 cn.onRender(this.el, null);
4064 * register a Navigation item
4065 * @param {Roo.bootstrap.NavItem} the navitem to add
4067 register : function(item)
4069 this.navItems.push( item);
4070 item.navId = this.navId;
4075 * clear all the Navigation item
4078 clearAll : function()
4081 this.el.dom.innerHTML = '';
4084 getNavItem: function(tabId)
4087 Roo.each(this.navItems, function(e) {
4088 if (e.tabId == tabId) {
4098 setActiveNext : function()
4100 var i = this.indexOfNav(this.getActive());
4101 if (i > this.navItems.length) {
4104 this.setActiveItem(this.navItems[i+1]);
4106 setActivePrev : function()
4108 var i = this.indexOfNav(this.getActive());
4112 this.setActiveItem(this.navItems[i-1]);
4114 clearWasActive : function(except) {
4115 Roo.each(this.navItems, function(e) {
4116 if (e.tabId != except.tabId && e.was_active) {
4117 e.was_active = false;
4124 getWasActive : function ()
4127 Roo.each(this.navItems, function(e) {
4142 Roo.apply(Roo.bootstrap.NavGroup, {
4146 * register a Navigation Group
4147 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4149 register : function(navgrp)
4151 this.groups[navgrp.navId] = navgrp;
4155 * fetch a Navigation Group based on the navigation ID
4156 * @param {string} the navgroup to add
4157 * @returns {Roo.bootstrap.NavGroup} the navgroup
4159 get: function(navId) {
4160 if (typeof(this.groups[navId]) == 'undefined') {
4162 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4164 return this.groups[navId] ;
4179 * @class Roo.bootstrap.NavItem
4180 * @extends Roo.bootstrap.Component
4181 * Bootstrap Navbar.NavItem class
4182 * @cfg {String} href link to
4183 * @cfg {String} html content of button
4184 * @cfg {String} badge text inside badge
4185 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4186 * @cfg {String} glyphicon name of glyphicon
4187 * @cfg {String} icon name of font awesome icon
4188 * @cfg {Boolean} active Is item active
4189 * @cfg {Boolean} disabled Is item disabled
4191 * @cfg {Boolean} preventDefault (true | false) default false
4192 * @cfg {String} tabId the tab that this item activates.
4193 * @cfg {String} tagtype (a|span) render as a href or span?
4194 * @cfg {Boolean} animateRef (true|false) link to element default false
4197 * Create a new Navbar Item
4198 * @param {Object} config The config object
4200 Roo.bootstrap.NavItem = function(config){
4201 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4206 * The raw click event for the entire grid.
4207 * @param {Roo.EventObject} e
4212 * Fires when the active item active state changes
4213 * @param {Roo.bootstrap.NavItem} this
4214 * @param {boolean} state the new state
4220 * Fires when scroll to element
4221 * @param {Roo.bootstrap.NavItem} this
4222 * @param {Object} options
4223 * @param {Roo.EventObject} e
4231 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4239 preventDefault : false,
4246 getAutoCreate : function(){
4255 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4257 if (this.disabled) {
4258 cfg.cls += ' disabled';
4261 if (this.href || this.html || this.glyphicon || this.icon) {
4265 href : this.href || "#",
4266 html: this.html || ''
4271 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4274 if(this.glyphicon) {
4275 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4280 cfg.cn[0].html += " <span class='caret'></span>";
4284 if (this.badge !== '') {
4286 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4294 initEvents: function()
4296 if (typeof (this.menu) != 'undefined') {
4297 this.menu.parentType = this.xtype;
4298 this.menu.triggerEl = this.el;
4299 this.menu = this.addxtype(Roo.apply({}, this.menu));
4302 this.el.select('a',true).on('click', this.onClick, this);
4304 if(this.tagtype == 'span'){
4305 this.el.select('span',true).on('click', this.onClick, this);
4308 // at this point parent should be available..
4309 this.parent().register(this);
4312 onClick : function(e)
4314 if (e.getTarget('.dropdown-menu-item')) {
4315 // did you click on a menu itemm.... - then don't trigger onclick..
4320 this.preventDefault ||
4323 Roo.log("NavItem - prevent Default?");
4327 if (this.disabled) {
4331 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4332 if (tg && tg.transition) {
4333 Roo.log("waiting for the transitionend");
4339 //Roo.log("fire event clicked");
4340 if(this.fireEvent('click', this, e) === false){
4344 if(this.tagtype == 'span'){
4348 //Roo.log(this.href);
4349 var ael = this.el.select('a',true).first();
4352 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4353 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4354 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4355 return; // ignore... - it's a 'hash' to another page.
4357 Roo.log("NavItem - prevent Default?");
4359 this.scrollToElement(e);
4363 var p = this.parent();
4365 if (['tabs','pills'].indexOf(p.type)!==-1) {
4366 if (typeof(p.setActiveItem) !== 'undefined') {
4367 p.setActiveItem(this);
4371 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4372 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4373 // remove the collapsed menu expand...
4374 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4378 isActive: function () {
4381 setActive : function(state, fire, is_was_active)
4383 if (this.active && !state && this.navId) {
4384 this.was_active = true;
4385 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4387 nv.clearWasActive(this);
4391 this.active = state;
4394 this.el.removeClass('active');
4395 } else if (!this.el.hasClass('active')) {
4396 this.el.addClass('active');
4399 this.fireEvent('changed', this, state);
4402 // show a panel if it's registered and related..
4404 if (!this.navId || !this.tabId || !state || is_was_active) {
4408 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4412 var pan = tg.getPanelByName(this.tabId);
4416 // if we can not flip to new panel - go back to old nav highlight..
4417 if (false == tg.showPanel(pan)) {
4418 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4420 var onav = nv.getWasActive();
4422 onav.setActive(true, false, true);
4431 // this should not be here...
4432 setDisabled : function(state)
4434 this.disabled = state;
4436 this.el.removeClass('disabled');
4437 } else if (!this.el.hasClass('disabled')) {
4438 this.el.addClass('disabled');
4444 * Fetch the element to display the tooltip on.
4445 * @return {Roo.Element} defaults to this.el
4447 tooltipEl : function()
4449 return this.el.select('' + this.tagtype + '', true).first();
4452 scrollToElement : function(e)
4454 var c = document.body;
4457 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4459 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4460 c = document.documentElement;
4463 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4469 var o = target.calcOffsetsTo(c);
4476 this.fireEvent('scrollto', this, options, e);
4478 Roo.get(c).scrollTo('top', options.value, true);
4491 * <span> icon </span>
4492 * <span> text </span>
4493 * <span>badge </span>
4497 * @class Roo.bootstrap.NavSidebarItem
4498 * @extends Roo.bootstrap.NavItem
4499 * Bootstrap Navbar.NavSidebarItem class
4500 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4501 * {bool} open is the menu open
4503 * Create a new Navbar Button
4504 * @param {Object} config The config object
4506 Roo.bootstrap.NavSidebarItem = function(config){
4507 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4512 * The raw click event for the entire grid.
4513 * @param {Roo.EventObject} e
4518 * Fires when the active item active state changes
4519 * @param {Roo.bootstrap.NavSidebarItem} this
4520 * @param {boolean} state the new state
4528 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4530 badgeWeight : 'default',
4534 getAutoCreate : function(){
4539 href : this.href || '#',
4551 html : this.html || ''
4556 cfg.cls += ' active';
4559 if (this.disabled) {
4560 cfg.cls += ' disabled';
4563 cfg.cls += ' open x-open';
4566 if (this.glyphicon || this.icon) {
4567 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4568 a.cn.push({ tag : 'i', cls : c }) ;
4573 if (this.badge !== '') {
4575 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4579 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4580 a.cls += 'dropdown-toggle treeview' ;
4588 initEvents : function()
4590 if (typeof (this.menu) != 'undefined') {
4591 this.menu.parentType = this.xtype;
4592 this.menu.triggerEl = this.el;
4593 this.menu = this.addxtype(Roo.apply({}, this.menu));
4596 this.el.on('click', this.onClick, this);
4599 if(this.badge !== ''){
4601 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4606 onClick : function(e)
4613 if(this.preventDefault){
4617 this.fireEvent('click', this);
4620 disable : function()
4622 this.setDisabled(true);
4627 this.setDisabled(false);
4630 setDisabled : function(state)
4632 if(this.disabled == state){
4636 this.disabled = state;
4639 this.el.addClass('disabled');
4643 this.el.removeClass('disabled');
4648 setActive : function(state)
4650 if(this.active == state){
4654 this.active = state;
4657 this.el.addClass('active');
4661 this.el.removeClass('active');
4666 isActive: function ()
4671 setBadge : function(str)
4677 this.badgeEl.dom.innerHTML = str;
4694 * @class Roo.bootstrap.Row
4695 * @extends Roo.bootstrap.Component
4696 * Bootstrap Row class (contains columns...)
4700 * @param {Object} config The config object
4703 Roo.bootstrap.Row = function(config){
4704 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4707 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4709 getAutoCreate : function(){
4728 * @class Roo.bootstrap.Element
4729 * @extends Roo.bootstrap.Component
4730 * Bootstrap Element class
4731 * @cfg {String} html contents of the element
4732 * @cfg {String} tag tag of the element
4733 * @cfg {String} cls class of the element
4734 * @cfg {Boolean} preventDefault (true|false) default false
4735 * @cfg {Boolean} clickable (true|false) default false
4738 * Create a new Element
4739 * @param {Object} config The config object
4742 Roo.bootstrap.Element = function(config){
4743 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4749 * When a element is chick
4750 * @param {Roo.bootstrap.Element} this
4751 * @param {Roo.EventObject} e
4757 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4762 preventDefault: false,
4765 getAutoCreate : function(){
4776 initEvents: function()
4778 Roo.bootstrap.Element.superclass.initEvents.call(this);
4781 this.el.on('click', this.onClick, this);
4786 onClick : function(e)
4788 if(this.preventDefault){
4792 this.fireEvent('click', this, e);
4795 getValue : function()
4797 return this.el.dom.innerHTML;
4800 setValue : function(value)
4802 this.el.dom.innerHTML = value;
4817 * @class Roo.bootstrap.Pagination
4818 * @extends Roo.bootstrap.Component
4819 * Bootstrap Pagination class
4820 * @cfg {String} size xs | sm | md | lg
4821 * @cfg {Boolean} inverse false | true
4824 * Create a new Pagination
4825 * @param {Object} config The config object
4828 Roo.bootstrap.Pagination = function(config){
4829 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4832 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4838 getAutoCreate : function(){
4844 cfg.cls += ' inverse';
4850 cfg.cls += " " + this.cls;
4868 * @class Roo.bootstrap.PaginationItem
4869 * @extends Roo.bootstrap.Component
4870 * Bootstrap PaginationItem class
4871 * @cfg {String} html text
4872 * @cfg {String} href the link
4873 * @cfg {Boolean} preventDefault (true | false) default true
4874 * @cfg {Boolean} active (true | false) default false
4875 * @cfg {Boolean} disabled default false
4879 * Create a new PaginationItem
4880 * @param {Object} config The config object
4884 Roo.bootstrap.PaginationItem = function(config){
4885 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4890 * The raw click event for the entire grid.
4891 * @param {Roo.EventObject} e
4897 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4901 preventDefault: true,
4906 getAutoCreate : function(){
4912 href : this.href ? this.href : '#',
4913 html : this.html ? this.html : ''
4923 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4927 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4933 initEvents: function() {
4935 this.el.on('click', this.onClick, this);
4938 onClick : function(e)
4940 Roo.log('PaginationItem on click ');
4941 if(this.preventDefault){
4949 this.fireEvent('click', this, e);
4965 * @class Roo.bootstrap.Slider
4966 * @extends Roo.bootstrap.Component
4967 * Bootstrap Slider class
4970 * Create a new Slider
4971 * @param {Object} config The config object
4974 Roo.bootstrap.Slider = function(config){
4975 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4978 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4980 getAutoCreate : function(){
4984 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4988 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5000 * Ext JS Library 1.1.1
5001 * Copyright(c) 2006-2007, Ext JS, LLC.
5003 * Originally Released Under LGPL - original licence link has changed is not relivant.
5006 * <script type="text/javascript">
5011 * @class Roo.grid.ColumnModel
5012 * @extends Roo.util.Observable
5013 * This is the default implementation of a ColumnModel used by the Grid. It defines
5014 * the columns in the grid.
5017 var colModel = new Roo.grid.ColumnModel([
5018 {header: "Ticker", width: 60, sortable: true, locked: true},
5019 {header: "Company Name", width: 150, sortable: true},
5020 {header: "Market Cap.", width: 100, sortable: true},
5021 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5022 {header: "Employees", width: 100, sortable: true, resizable: false}
5027 * The config options listed for this class are options which may appear in each
5028 * individual column definition.
5029 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5031 * @param {Object} config An Array of column config objects. See this class's
5032 * config objects for details.
5034 Roo.grid.ColumnModel = function(config){
5036 * The config passed into the constructor
5038 this.config = config;
5041 // if no id, create one
5042 // if the column does not have a dataIndex mapping,
5043 // map it to the order it is in the config
5044 for(var i = 0, len = config.length; i < len; i++){
5046 if(typeof c.dataIndex == "undefined"){
5049 if(typeof c.renderer == "string"){
5050 c.renderer = Roo.util.Format[c.renderer];
5052 if(typeof c.id == "undefined"){
5055 if(c.editor && c.editor.xtype){
5056 c.editor = Roo.factory(c.editor, Roo.grid);
5058 if(c.editor && c.editor.isFormField){
5059 c.editor = new Roo.grid.GridEditor(c.editor);
5061 this.lookup[c.id] = c;
5065 * The width of columns which have no width specified (defaults to 100)
5068 this.defaultWidth = 100;
5071 * Default sortable of columns which have no sortable specified (defaults to false)
5074 this.defaultSortable = false;
5078 * @event widthchange
5079 * Fires when the width of a column changes.
5080 * @param {ColumnModel} this
5081 * @param {Number} columnIndex The column index
5082 * @param {Number} newWidth The new width
5084 "widthchange": true,
5086 * @event headerchange
5087 * Fires when the text of a header changes.
5088 * @param {ColumnModel} this
5089 * @param {Number} columnIndex The column index
5090 * @param {Number} newText The new header text
5092 "headerchange": true,
5094 * @event hiddenchange
5095 * Fires when a column is hidden or "unhidden".
5096 * @param {ColumnModel} this
5097 * @param {Number} columnIndex The column index
5098 * @param {Boolean} hidden true if hidden, false otherwise
5100 "hiddenchange": true,
5102 * @event columnmoved
5103 * Fires when a column is moved.
5104 * @param {ColumnModel} this
5105 * @param {Number} oldIndex
5106 * @param {Number} newIndex
5108 "columnmoved" : true,
5110 * @event columlockchange
5111 * Fires when a column's locked state is changed
5112 * @param {ColumnModel} this
5113 * @param {Number} colIndex
5114 * @param {Boolean} locked true if locked
5116 "columnlockchange" : true
5118 Roo.grid.ColumnModel.superclass.constructor.call(this);
5120 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5122 * @cfg {String} header The header text to display in the Grid view.
5125 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5126 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5127 * specified, the column's index is used as an index into the Record's data Array.
5130 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5131 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5134 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5135 * Defaults to the value of the {@link #defaultSortable} property.
5136 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5139 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5142 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5145 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5148 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5151 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5152 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5153 * default renderer uses the raw data value. If an object is returned (bootstrap only)
5154 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5157 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5160 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5163 * @cfg {String} cursor (Optional)
5166 * @cfg {String} tooltip (Optional)
5169 * @cfg {Number} xs (Optional)
5172 * @cfg {Number} sm (Optional)
5175 * @cfg {Number} md (Optional)
5178 * @cfg {Number} lg (Optional)
5181 * Returns the id of the column at the specified index.
5182 * @param {Number} index The column index
5183 * @return {String} the id
5185 getColumnId : function(index){
5186 return this.config[index].id;
5190 * Returns the column for a specified id.
5191 * @param {String} id The column id
5192 * @return {Object} the column
5194 getColumnById : function(id){
5195 return this.lookup[id];
5200 * Returns the column for a specified dataIndex.
5201 * @param {String} dataIndex The column dataIndex
5202 * @return {Object|Boolean} the column or false if not found
5204 getColumnByDataIndex: function(dataIndex){
5205 var index = this.findColumnIndex(dataIndex);
5206 return index > -1 ? this.config[index] : false;
5210 * Returns the index for a specified column id.
5211 * @param {String} id The column id
5212 * @return {Number} the index, or -1 if not found
5214 getIndexById : function(id){
5215 for(var i = 0, len = this.config.length; i < len; i++){
5216 if(this.config[i].id == id){
5224 * Returns the index for a specified column dataIndex.
5225 * @param {String} dataIndex The column dataIndex
5226 * @return {Number} the index, or -1 if not found
5229 findColumnIndex : function(dataIndex){
5230 for(var i = 0, len = this.config.length; i < len; i++){
5231 if(this.config[i].dataIndex == dataIndex){
5239 moveColumn : function(oldIndex, newIndex){
5240 var c = this.config[oldIndex];
5241 this.config.splice(oldIndex, 1);
5242 this.config.splice(newIndex, 0, c);
5243 this.dataMap = null;
5244 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5247 isLocked : function(colIndex){
5248 return this.config[colIndex].locked === true;
5251 setLocked : function(colIndex, value, suppressEvent){
5252 if(this.isLocked(colIndex) == value){
5255 this.config[colIndex].locked = value;
5257 this.fireEvent("columnlockchange", this, colIndex, value);
5261 getTotalLockedWidth : function(){
5263 for(var i = 0; i < this.config.length; i++){
5264 if(this.isLocked(i) && !this.isHidden(i)){
5265 this.totalWidth += this.getColumnWidth(i);
5271 getLockedCount : function(){
5272 for(var i = 0, len = this.config.length; i < len; i++){
5273 if(!this.isLocked(i)){
5278 return this.config.length;
5282 * Returns the number of columns.
5285 getColumnCount : function(visibleOnly){
5286 if(visibleOnly === true){
5288 for(var i = 0, len = this.config.length; i < len; i++){
5289 if(!this.isHidden(i)){
5295 return this.config.length;
5299 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5300 * @param {Function} fn
5301 * @param {Object} scope (optional)
5302 * @return {Array} result
5304 getColumnsBy : function(fn, scope){
5306 for(var i = 0, len = this.config.length; i < len; i++){
5307 var c = this.config[i];
5308 if(fn.call(scope||this, c, i) === true){
5316 * Returns true if the specified column is sortable.
5317 * @param {Number} col The column index
5320 isSortable : function(col){
5321 if(typeof this.config[col].sortable == "undefined"){
5322 return this.defaultSortable;
5324 return this.config[col].sortable;
5328 * Returns the rendering (formatting) function defined for the column.
5329 * @param {Number} col The column index.
5330 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5332 getRenderer : function(col){
5333 if(!this.config[col].renderer){
5334 return Roo.grid.ColumnModel.defaultRenderer;
5336 return this.config[col].renderer;
5340 * Sets the rendering (formatting) function for a column.
5341 * @param {Number} col The column index
5342 * @param {Function} fn The function to use to process the cell's raw data
5343 * to return HTML markup for the grid view. The render function is called with
5344 * the following parameters:<ul>
5345 * <li>Data value.</li>
5346 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5347 * <li>css A CSS style string to apply to the table cell.</li>
5348 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5349 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5350 * <li>Row index</li>
5351 * <li>Column index</li>
5352 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5354 setRenderer : function(col, fn){
5355 this.config[col].renderer = fn;
5359 * Returns the width for the specified column.
5360 * @param {Number} col The column index
5363 getColumnWidth : function(col){
5364 return this.config[col].width * 1 || this.defaultWidth;
5368 * Sets the width for a column.
5369 * @param {Number} col The column index
5370 * @param {Number} width The new width
5372 setColumnWidth : function(col, width, suppressEvent){
5373 this.config[col].width = width;
5374 this.totalWidth = null;
5376 this.fireEvent("widthchange", this, col, width);
5381 * Returns the total width of all columns.
5382 * @param {Boolean} includeHidden True to include hidden column widths
5385 getTotalWidth : function(includeHidden){
5386 if(!this.totalWidth){
5387 this.totalWidth = 0;
5388 for(var i = 0, len = this.config.length; i < len; i++){
5389 if(includeHidden || !this.isHidden(i)){
5390 this.totalWidth += this.getColumnWidth(i);
5394 return this.totalWidth;
5398 * Returns the header for the specified column.
5399 * @param {Number} col The column index
5402 getColumnHeader : function(col){
5403 return this.config[col].header;
5407 * Sets the header for a column.
5408 * @param {Number} col The column index
5409 * @param {String} header The new header
5411 setColumnHeader : function(col, header){
5412 this.config[col].header = header;
5413 this.fireEvent("headerchange", this, col, header);
5417 * Returns the tooltip for the specified column.
5418 * @param {Number} col The column index
5421 getColumnTooltip : function(col){
5422 return this.config[col].tooltip;
5425 * Sets the tooltip for a column.
5426 * @param {Number} col The column index
5427 * @param {String} tooltip The new tooltip
5429 setColumnTooltip : function(col, tooltip){
5430 this.config[col].tooltip = tooltip;
5434 * Returns the dataIndex for the specified column.
5435 * @param {Number} col The column index
5438 getDataIndex : function(col){
5439 return this.config[col].dataIndex;
5443 * Sets the dataIndex for a column.
5444 * @param {Number} col The column index
5445 * @param {Number} dataIndex The new dataIndex
5447 setDataIndex : function(col, dataIndex){
5448 this.config[col].dataIndex = dataIndex;
5454 * Returns true if the cell is editable.
5455 * @param {Number} colIndex The column index
5456 * @param {Number} rowIndex The row index - this is nto actually used..?
5459 isCellEditable : function(colIndex, rowIndex){
5460 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5464 * Returns the editor defined for the cell/column.
5465 * return false or null to disable editing.
5466 * @param {Number} colIndex The column index
5467 * @param {Number} rowIndex The row index
5470 getCellEditor : function(colIndex, rowIndex){
5471 return this.config[colIndex].editor;
5475 * Sets if a column is editable.
5476 * @param {Number} col The column index
5477 * @param {Boolean} editable True if the column is editable
5479 setEditable : function(col, editable){
5480 this.config[col].editable = editable;
5485 * Returns true if the column is hidden.
5486 * @param {Number} colIndex The column index
5489 isHidden : function(colIndex){
5490 return this.config[colIndex].hidden;
5495 * Returns true if the column width cannot be changed
5497 isFixed : function(colIndex){
5498 return this.config[colIndex].fixed;
5502 * Returns true if the column can be resized
5505 isResizable : function(colIndex){
5506 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5509 * Sets if a column is hidden.
5510 * @param {Number} colIndex The column index
5511 * @param {Boolean} hidden True if the column is hidden
5513 setHidden : function(colIndex, hidden){
5514 this.config[colIndex].hidden = hidden;
5515 this.totalWidth = null;
5516 this.fireEvent("hiddenchange", this, colIndex, hidden);
5520 * Sets the editor for a column.
5521 * @param {Number} col The column index
5522 * @param {Object} editor The editor object
5524 setEditor : function(col, editor){
5525 this.config[col].editor = editor;
5529 Roo.grid.ColumnModel.defaultRenderer = function(value){
5530 if(typeof value == "string" && value.length < 1){
5536 // Alias for backwards compatibility
5537 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5540 * Ext JS Library 1.1.1
5541 * Copyright(c) 2006-2007, Ext JS, LLC.
5543 * Originally Released Under LGPL - original licence link has changed is not relivant.
5546 * <script type="text/javascript">
5550 * @class Roo.LoadMask
5551 * A simple utility class for generically masking elements while loading data. If the element being masked has
5552 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5553 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5554 * element's UpdateManager load indicator and will be destroyed after the initial load.
5556 * Create a new LoadMask
5557 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5558 * @param {Object} config The config object
5560 Roo.LoadMask = function(el, config){
5561 this.el = Roo.get(el);
5562 Roo.apply(this, config);
5564 this.store.on('beforeload', this.onBeforeLoad, this);
5565 this.store.on('load', this.onLoad, this);
5566 this.store.on('loadexception', this.onLoadException, this);
5567 this.removeMask = false;
5569 var um = this.el.getUpdateManager();
5570 um.showLoadIndicator = false; // disable the default indicator
5571 um.on('beforeupdate', this.onBeforeLoad, this);
5572 um.on('update', this.onLoad, this);
5573 um.on('failure', this.onLoad, this);
5574 this.removeMask = true;
5578 Roo.LoadMask.prototype = {
5580 * @cfg {Boolean} removeMask
5581 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5582 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5586 * The text to display in a centered loading message box (defaults to 'Loading...')
5590 * @cfg {String} msgCls
5591 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5593 msgCls : 'x-mask-loading',
5596 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5602 * Disables the mask to prevent it from being displayed
5604 disable : function(){
5605 this.disabled = true;
5609 * Enables the mask so that it can be displayed
5611 enable : function(){
5612 this.disabled = false;
5615 onLoadException : function()
5619 if (typeof(arguments[3]) != 'undefined') {
5620 Roo.MessageBox.alert("Error loading",arguments[3]);
5624 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5625 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5634 this.el.unmask(this.removeMask);
5639 this.el.unmask(this.removeMask);
5643 onBeforeLoad : function(){
5645 this.el.mask(this.msg, this.msgCls);
5650 destroy : function(){
5652 this.store.un('beforeload', this.onBeforeLoad, this);
5653 this.store.un('load', this.onLoad, this);
5654 this.store.un('loadexception', this.onLoadException, this);
5656 var um = this.el.getUpdateManager();
5657 um.un('beforeupdate', this.onBeforeLoad, this);
5658 um.un('update', this.onLoad, this);
5659 um.un('failure', this.onLoad, this);
5670 * @class Roo.bootstrap.Table
5671 * @extends Roo.bootstrap.Component
5672 * Bootstrap Table class
5673 * @cfg {String} cls table class
5674 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5675 * @cfg {String} bgcolor Specifies the background color for a table
5676 * @cfg {Number} border Specifies whether the table cells should have borders or not
5677 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5678 * @cfg {Number} cellspacing Specifies the space between cells
5679 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5680 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5681 * @cfg {String} sortable Specifies that the table should be sortable
5682 * @cfg {String} summary Specifies a summary of the content of a table
5683 * @cfg {Number} width Specifies the width of a table
5684 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5686 * @cfg {boolean} striped Should the rows be alternative striped
5687 * @cfg {boolean} bordered Add borders to the table
5688 * @cfg {boolean} hover Add hover highlighting
5689 * @cfg {boolean} condensed Format condensed
5690 * @cfg {boolean} responsive Format condensed
5691 * @cfg {Boolean} loadMask (true|false) default false
5692 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5693 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5694 * @cfg {Boolean} rowSelection (true|false) default false
5695 * @cfg {Boolean} cellSelection (true|false) default false
5696 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5697 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5701 * Create a new Table
5702 * @param {Object} config The config object
5705 Roo.bootstrap.Table = function(config){
5706 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5711 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5712 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5713 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5714 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5718 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5719 this.sm = this.selModel;
5720 this.sm.xmodule = this.xmodule || false;
5722 if (this.cm && typeof(this.cm.config) == 'undefined') {
5723 this.colModel = new Roo.grid.ColumnModel(this.cm);
5724 this.cm = this.colModel;
5725 this.cm.xmodule = this.xmodule || false;
5728 this.store= Roo.factory(this.store, Roo.data);
5729 this.ds = this.store;
5730 this.ds.xmodule = this.xmodule || false;
5733 if (this.footer && this.store) {
5734 this.footer.dataSource = this.ds;
5735 this.footer = Roo.factory(this.footer);
5742 * Fires when a cell is clicked
5743 * @param {Roo.bootstrap.Table} this
5744 * @param {Roo.Element} el
5745 * @param {Number} rowIndex
5746 * @param {Number} columnIndex
5747 * @param {Roo.EventObject} e
5751 * @event celldblclick
5752 * Fires when a cell is double clicked
5753 * @param {Roo.bootstrap.Table} this
5754 * @param {Roo.Element} el
5755 * @param {Number} rowIndex
5756 * @param {Number} columnIndex
5757 * @param {Roo.EventObject} e
5759 "celldblclick" : true,
5762 * Fires when a row is clicked
5763 * @param {Roo.bootstrap.Table} this
5764 * @param {Roo.Element} el
5765 * @param {Number} rowIndex
5766 * @param {Roo.EventObject} e
5770 * @event rowdblclick
5771 * Fires when a row is double clicked
5772 * @param {Roo.bootstrap.Table} this
5773 * @param {Roo.Element} el
5774 * @param {Number} rowIndex
5775 * @param {Roo.EventObject} e
5777 "rowdblclick" : true,
5780 * Fires when a mouseover occur
5781 * @param {Roo.bootstrap.Table} this
5782 * @param {Roo.Element} el
5783 * @param {Number} rowIndex
5784 * @param {Number} columnIndex
5785 * @param {Roo.EventObject} e
5790 * Fires when a mouseout occur
5791 * @param {Roo.bootstrap.Table} this
5792 * @param {Roo.Element} el
5793 * @param {Number} rowIndex
5794 * @param {Number} columnIndex
5795 * @param {Roo.EventObject} e
5800 * Fires when a row is rendered, so you can change add a style to it.
5801 * @param {Roo.bootstrap.Table} this
5802 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5806 * @event rowsrendered
5807 * Fires when all the rows have been rendered
5808 * @param {Roo.bootstrap.Table} this
5810 'rowsrendered' : true,
5812 * @event contextmenu
5813 * The raw contextmenu event for the entire grid.
5814 * @param {Roo.EventObject} e
5816 "contextmenu" : true,
5818 * @event rowcontextmenu
5819 * Fires when a row is right clicked
5820 * @param {Roo.bootstrap.Table} this
5821 * @param {Number} rowIndex
5822 * @param {Roo.EventObject} e
5824 "rowcontextmenu" : true,
5826 * @event cellcontextmenu
5827 * Fires when a cell is right clicked
5828 * @param {Roo.bootstrap.Table} this
5829 * @param {Number} rowIndex
5830 * @param {Number} cellIndex
5831 * @param {Roo.EventObject} e
5833 "cellcontextmenu" : true,
5835 * @event headercontextmenu
5836 * Fires when a header is right clicked
5837 * @param {Roo.bootstrap.Table} this
5838 * @param {Number} columnIndex
5839 * @param {Roo.EventObject} e
5841 "headercontextmenu" : true
5845 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5871 rowSelection : false,
5872 cellSelection : false,
5875 // Roo.Element - the tbody
5877 // Roo.Element - thead element
5880 container: false, // used by gridpanel...
5882 getAutoCreate : function()
5884 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5891 if (this.scrollBody) {
5892 cfg.cls += ' table-body-fixed';
5895 cfg.cls += ' table-striped';
5899 cfg.cls += ' table-hover';
5901 if (this.bordered) {
5902 cfg.cls += ' table-bordered';
5904 if (this.condensed) {
5905 cfg.cls += ' table-condensed';
5907 if (this.responsive) {
5908 cfg.cls += ' table-responsive';
5912 cfg.cls+= ' ' +this.cls;
5915 // this lot should be simplifed...
5918 cfg.align=this.align;
5921 cfg.bgcolor=this.bgcolor;
5924 cfg.border=this.border;
5926 if (this.cellpadding) {
5927 cfg.cellpadding=this.cellpadding;
5929 if (this.cellspacing) {
5930 cfg.cellspacing=this.cellspacing;
5933 cfg.frame=this.frame;
5936 cfg.rules=this.rules;
5938 if (this.sortable) {
5939 cfg.sortable=this.sortable;
5942 cfg.summary=this.summary;
5945 cfg.width=this.width;
5948 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5951 if(this.store || this.cm){
5952 if(this.headerShow){
5953 cfg.cn.push(this.renderHeader());
5956 cfg.cn.push(this.renderBody());
5958 if(this.footerShow){
5959 cfg.cn.push(this.renderFooter());
5961 // where does this come from?
5962 //cfg.cls+= ' TableGrid';
5965 return { cn : [ cfg ] };
5968 initEvents : function()
5970 if(!this.store || !this.cm){
5974 //Roo.log('initEvents with ds!!!!');
5976 this.mainBody = this.el.select('tbody', true).first();
5977 this.mainHead = this.el.select('thead', true).first();
5983 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5984 e.on('click', _this.sort, _this);
5987 this.el.on("click", this.onClick, this);
5988 this.el.on("dblclick", this.onDblClick, this);
5990 // why is this done????? = it breaks dialogs??
5991 //this.parent().el.setStyle('position', 'relative');
5995 this.footer.parentId = this.id;
5996 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5999 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6001 this.store.on('load', this.onLoad, this);
6002 this.store.on('beforeload', this.onBeforeLoad, this);
6003 this.store.on('update', this.onUpdate, this);
6004 this.store.on('add', this.onAdd, this);
6005 this.store.on("clear", this.clear, this);
6007 this.el.on("contextmenu", this.onContextMenu, this);
6009 this.mainBody.on('scroll', this.onBodyScroll, this);
6014 onContextMenu : function(e, t)
6016 this.processEvent("contextmenu", e);
6019 processEvent : function(name, e)
6021 if (name != 'touchstart' ) {
6022 this.fireEvent(name, e);
6025 var t = e.getTarget();
6027 var cell = Roo.get(t);
6033 if(cell.findParent('tfoot', false, true)){
6037 if(cell.findParent('thead', false, true)){
6039 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6040 cell = Roo.get(t).findParent('th', false, true);
6042 Roo.log("failed to find th in thead?");
6043 Roo.log(e.getTarget());
6048 var cellIndex = cell.dom.cellIndex;
6050 var ename = name == 'touchstart' ? 'click' : name;
6051 this.fireEvent("header" + ename, this, cellIndex, e);
6056 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6057 cell = Roo.get(t).findParent('td', false, true);
6059 Roo.log("failed to find th in tbody?");
6060 Roo.log(e.getTarget());
6065 var row = cell.findParent('tr', false, true);
6066 var cellIndex = cell.dom.cellIndex;
6067 var rowIndex = row.dom.rowIndex - 1;
6071 this.fireEvent("row" + name, this, rowIndex, e);
6075 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6081 onMouseover : function(e, el)
6083 var cell = Roo.get(el);
6089 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6090 cell = cell.findParent('td', false, true);
6093 var row = cell.findParent('tr', false, true);
6094 var cellIndex = cell.dom.cellIndex;
6095 var rowIndex = row.dom.rowIndex - 1; // start from 0
6097 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6101 onMouseout : function(e, el)
6103 var cell = Roo.get(el);
6109 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6110 cell = cell.findParent('td', false, true);
6113 var row = cell.findParent('tr', false, true);
6114 var cellIndex = cell.dom.cellIndex;
6115 var rowIndex = row.dom.rowIndex - 1; // start from 0
6117 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6121 onClick : function(e, el)
6123 var cell = Roo.get(el);
6125 if(!cell || (!this.cellSelection && !this.rowSelection)){
6129 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6130 cell = cell.findParent('td', false, true);
6133 if(!cell || typeof(cell) == 'undefined'){
6137 var row = cell.findParent('tr', false, true);
6139 if(!row || typeof(row) == 'undefined'){
6143 var cellIndex = cell.dom.cellIndex;
6144 var rowIndex = this.getRowIndex(row);
6146 // why??? - should these not be based on SelectionModel?
6147 if(this.cellSelection){
6148 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6151 if(this.rowSelection){
6152 this.fireEvent('rowclick', this, row, rowIndex, e);
6158 onDblClick : function(e,el)
6160 var cell = Roo.get(el);
6162 if(!cell || (!this.CellSelection && !this.RowSelection)){
6166 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6167 cell = cell.findParent('td', false, true);
6170 if(!cell || typeof(cell) == 'undefined'){
6174 var row = cell.findParent('tr', false, true);
6176 if(!row || typeof(row) == 'undefined'){
6180 var cellIndex = cell.dom.cellIndex;
6181 var rowIndex = this.getRowIndex(row);
6183 if(this.CellSelection){
6184 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6187 if(this.RowSelection){
6188 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6192 sort : function(e,el)
6194 var col = Roo.get(el);
6196 if(!col.hasClass('sortable')){
6200 var sort = col.attr('sort');
6203 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6207 this.store.sortInfo = {field : sort, direction : dir};
6210 Roo.log("calling footer first");
6211 this.footer.onClick('first');
6214 this.store.load({ params : { start : 0 } });
6218 renderHeader : function()
6226 this.totalWidth = 0;
6228 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6230 var config = cm.config[i];
6235 html: cm.getColumnHeader(i)
6240 if(typeof(config.sortable) != 'undefined' && config.sortable){
6242 c.html = '<i class="glyphicon"></i>' + c.html;
6245 if(typeof(config.lgHeader) != 'undefined'){
6246 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6249 if(typeof(config.mdHeader) != 'undefined'){
6250 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6253 if(typeof(config.smHeader) != 'undefined'){
6254 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6257 if(typeof(config.xsHeader) != 'undefined'){
6258 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6265 if(typeof(config.tooltip) != 'undefined'){
6266 c.tooltip = config.tooltip;
6269 if(typeof(config.colspan) != 'undefined'){
6270 c.colspan = config.colspan;
6273 if(typeof(config.hidden) != 'undefined' && config.hidden){
6274 c.style += ' display:none;';
6277 if(typeof(config.dataIndex) != 'undefined'){
6278 c.sort = config.dataIndex;
6283 if(typeof(config.align) != 'undefined' && config.align.length){
6284 c.style += ' text-align:' + config.align + ';';
6287 if(typeof(config.width) != 'undefined'){
6288 c.style += ' width:' + config.width + 'px;';
6289 this.totalWidth += config.width;
6291 this.totalWidth += 100; // assume minimum of 100 per column?
6294 if(typeof(config.cls) != 'undefined'){
6295 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6298 ['xs','sm','md','lg'].map(function(size){
6300 if(typeof(config[size]) == 'undefined'){
6304 if (!config[size]) { // 0 = hidden
6305 c.cls += ' hidden-' + size;
6309 c.cls += ' col-' + size + '-' + config[size];
6319 renderBody : function()
6329 colspan : this.cm.getColumnCount()
6339 renderFooter : function()
6349 colspan : this.cm.getColumnCount()
6363 // Roo.log('ds onload');
6368 var ds = this.store;
6370 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6371 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6372 if (_this.store.sortInfo) {
6374 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6375 e.select('i', true).addClass(['glyphicon-arrow-up']);
6378 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6379 e.select('i', true).addClass(['glyphicon-arrow-down']);
6384 var tbody = this.mainBody;
6386 if(ds.getCount() > 0){
6387 ds.data.each(function(d,rowIndex){
6388 var row = this.renderRow(cm, ds, rowIndex);
6390 tbody.createChild(row);
6394 if(row.cellObjects.length){
6395 Roo.each(row.cellObjects, function(r){
6396 _this.renderCellObject(r);
6403 Roo.each(this.el.select('tbody td', true).elements, function(e){
6404 e.on('mouseover', _this.onMouseover, _this);
6407 Roo.each(this.el.select('tbody td', true).elements, function(e){
6408 e.on('mouseout', _this.onMouseout, _this);
6410 this.fireEvent('rowsrendered', this);
6411 //if(this.loadMask){
6412 // this.maskEl.hide();
6419 onUpdate : function(ds,record)
6421 this.refreshRow(record);
6424 onRemove : function(ds, record, index, isUpdate){
6425 if(isUpdate !== true){
6426 this.fireEvent("beforerowremoved", this, index, record);
6428 var bt = this.mainBody.dom;
6430 var rows = this.el.select('tbody > tr', true).elements;
6432 if(typeof(rows[index]) != 'undefined'){
6433 bt.removeChild(rows[index].dom);
6436 // if(bt.rows[index]){
6437 // bt.removeChild(bt.rows[index]);
6440 if(isUpdate !== true){
6441 //this.stripeRows(index);
6442 //this.syncRowHeights(index, index);
6444 this.fireEvent("rowremoved", this, index, record);
6448 onAdd : function(ds, records, rowIndex)
6450 //Roo.log('on Add called');
6451 // - note this does not handle multiple adding very well..
6452 var bt = this.mainBody.dom;
6453 for (var i =0 ; i < records.length;i++) {
6454 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6455 //Roo.log(records[i]);
6456 //Roo.log(this.store.getAt(rowIndex+i));
6457 this.insertRow(this.store, rowIndex + i, false);
6464 refreshRow : function(record){
6465 var ds = this.store, index;
6466 if(typeof record == 'number'){
6468 record = ds.getAt(index);
6470 index = ds.indexOf(record);
6472 this.insertRow(ds, index, true);
6473 this.onRemove(ds, record, index+1, true);
6474 //this.syncRowHeights(index, index);
6476 this.fireEvent("rowupdated", this, index, record);
6479 insertRow : function(dm, rowIndex, isUpdate){
6482 this.fireEvent("beforerowsinserted", this, rowIndex);
6484 //var s = this.getScrollState();
6485 var row = this.renderRow(this.cm, this.store, rowIndex);
6486 // insert before rowIndex..
6487 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6491 if(row.cellObjects.length){
6492 Roo.each(row.cellObjects, function(r){
6493 _this.renderCellObject(r);
6498 this.fireEvent("rowsinserted", this, rowIndex);
6499 //this.syncRowHeights(firstRow, lastRow);
6500 //this.stripeRows(firstRow);
6507 getRowDom : function(rowIndex)
6509 var rows = this.el.select('tbody > tr', true).elements;
6511 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6514 // returns the object tree for a tr..
6517 renderRow : function(cm, ds, rowIndex)
6520 var d = ds.getAt(rowIndex);
6527 var cellObjects = [];
6529 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6530 var config = cm.config[i];
6532 var renderer = cm.getRenderer(i);
6536 if(typeof(renderer) !== 'undefined'){
6537 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6539 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6540 // and are rendered into the cells after the row is rendered - using the id for the element.
6542 if(typeof(value) === 'object'){
6552 rowIndex : rowIndex,
6557 this.fireEvent('rowclass', this, rowcfg);
6561 cls : rowcfg.rowClass,
6563 html: (typeof(value) === 'object') ? '' : value
6570 if(typeof(config.colspan) != 'undefined'){
6571 td.colspan = config.colspan;
6574 if(typeof(config.hidden) != 'undefined' && config.hidden){
6575 td.style += ' display:none;';
6578 if(typeof(config.align) != 'undefined' && config.align.length){
6579 td.style += ' text-align:' + config.align + ';';
6582 if(typeof(config.width) != 'undefined'){
6583 td.style += ' width:' + config.width + 'px;';
6586 if(typeof(config.cursor) != 'undefined'){
6587 td.style += ' cursor:' + config.cursor + ';';
6590 if(typeof(config.cls) != 'undefined'){
6591 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6594 ['xs','sm','md','lg'].map(function(size){
6596 if(typeof(config[size]) == 'undefined'){
6600 if (!config[size]) { // 0 = hidden
6601 td.cls += ' hidden-' + size;
6605 td.cls += ' col-' + size + '-' + config[size];
6613 row.cellObjects = cellObjects;
6621 onBeforeLoad : function()
6623 //Roo.log('ds onBeforeLoad');
6627 //if(this.loadMask){
6628 // this.maskEl.show();
6636 this.el.select('tbody', true).first().dom.innerHTML = '';
6639 * Show or hide a row.
6640 * @param {Number} rowIndex to show or hide
6641 * @param {Boolean} state hide
6643 setRowVisibility : function(rowIndex, state)
6645 var bt = this.mainBody.dom;
6647 var rows = this.el.select('tbody > tr', true).elements;
6649 if(typeof(rows[rowIndex]) == 'undefined'){
6652 rows[rowIndex].dom.style.display = state ? '' : 'none';
6656 getSelectionModel : function(){
6658 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6660 return this.selModel;
6663 * Render the Roo.bootstrap object from renderder
6665 renderCellObject : function(r)
6669 var t = r.cfg.render(r.container);
6672 Roo.each(r.cfg.cn, function(c){
6674 container: t.getChildContainer(),
6677 _this.renderCellObject(child);
6682 getRowIndex : function(row)
6686 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6697 * Returns the grid's underlying element = used by panel.Grid
6698 * @return {Element} The element
6700 getGridEl : function(){
6704 * Forces a resize - used by panel.Grid
6705 * @return {Element} The element
6707 autoSize : function()
6709 //var ctr = Roo.get(this.container.dom.parentElement);
6710 var ctr = Roo.get(this.el.dom);
6712 var thd = this.getGridEl().select('thead',true).first();
6713 var tbd = this.getGridEl().select('tbody', true).first();
6716 var cw = ctr.getWidth();
6720 tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6721 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6724 cw = Math.max(cw, this.totalWidth);
6725 this.getGridEl().select('tr',true).setWidth(cw);
6726 // resize 'expandable coloumn?
6728 return; // we doe not have a view in this design..
6731 onBodyScroll: function()
6734 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6735 this.mainHead.setStyle({
6736 'position' : 'relative',
6737 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6754 * @class Roo.bootstrap.TableCell
6755 * @extends Roo.bootstrap.Component
6756 * Bootstrap TableCell class
6757 * @cfg {String} html cell contain text
6758 * @cfg {String} cls cell class
6759 * @cfg {String} tag cell tag (td|th) default td
6760 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6761 * @cfg {String} align Aligns the content in a cell
6762 * @cfg {String} axis Categorizes cells
6763 * @cfg {String} bgcolor Specifies the background color of a cell
6764 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6765 * @cfg {Number} colspan Specifies the number of columns a cell should span
6766 * @cfg {String} headers Specifies one or more header cells a cell is related to
6767 * @cfg {Number} height Sets the height of a cell
6768 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6769 * @cfg {Number} rowspan Sets the number of rows a cell should span
6770 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6771 * @cfg {String} valign Vertical aligns the content in a cell
6772 * @cfg {Number} width Specifies the width of a cell
6775 * Create a new TableCell
6776 * @param {Object} config The config object
6779 Roo.bootstrap.TableCell = function(config){
6780 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6783 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6803 getAutoCreate : function(){
6804 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6824 cfg.align=this.align
6830 cfg.bgcolor=this.bgcolor
6833 cfg.charoff=this.charoff
6836 cfg.colspan=this.colspan
6839 cfg.headers=this.headers
6842 cfg.height=this.height
6845 cfg.nowrap=this.nowrap
6848 cfg.rowspan=this.rowspan
6851 cfg.scope=this.scope
6854 cfg.valign=this.valign
6857 cfg.width=this.width
6876 * @class Roo.bootstrap.TableRow
6877 * @extends Roo.bootstrap.Component
6878 * Bootstrap TableRow class
6879 * @cfg {String} cls row class
6880 * @cfg {String} align Aligns the content in a table row
6881 * @cfg {String} bgcolor Specifies a background color for a table row
6882 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6883 * @cfg {String} valign Vertical aligns the content in a table row
6886 * Create a new TableRow
6887 * @param {Object} config The config object
6890 Roo.bootstrap.TableRow = function(config){
6891 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6894 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6902 getAutoCreate : function(){
6903 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6913 cfg.align = this.align;
6916 cfg.bgcolor = this.bgcolor;
6919 cfg.charoff = this.charoff;
6922 cfg.valign = this.valign;
6940 * @class Roo.bootstrap.TableBody
6941 * @extends Roo.bootstrap.Component
6942 * Bootstrap TableBody class
6943 * @cfg {String} cls element class
6944 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6945 * @cfg {String} align Aligns the content inside the element
6946 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6947 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6950 * Create a new TableBody
6951 * @param {Object} config The config object
6954 Roo.bootstrap.TableBody = function(config){
6955 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6958 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6966 getAutoCreate : function(){
6967 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6981 cfg.align = this.align;
6984 cfg.charoff = this.charoff;
6987 cfg.valign = this.valign;
6994 // initEvents : function()
7001 // this.store = Roo.factory(this.store, Roo.data);
7002 // this.store.on('load', this.onLoad, this);
7004 // this.store.load();
7008 // onLoad: function ()
7010 // this.fireEvent('load', this);
7020 * Ext JS Library 1.1.1
7021 * Copyright(c) 2006-2007, Ext JS, LLC.
7023 * Originally Released Under LGPL - original licence link has changed is not relivant.
7026 * <script type="text/javascript">
7029 // as we use this in bootstrap.
7030 Roo.namespace('Roo.form');
7032 * @class Roo.form.Action
7033 * Internal Class used to handle form actions
7035 * @param {Roo.form.BasicForm} el The form element or its id
7036 * @param {Object} config Configuration options
7041 // define the action interface
7042 Roo.form.Action = function(form, options){
7044 this.options = options || {};
7047 * Client Validation Failed
7050 Roo.form.Action.CLIENT_INVALID = 'client';
7052 * Server Validation Failed
7055 Roo.form.Action.SERVER_INVALID = 'server';
7057 * Connect to Server Failed
7060 Roo.form.Action.CONNECT_FAILURE = 'connect';
7062 * Reading Data from Server Failed
7065 Roo.form.Action.LOAD_FAILURE = 'load';
7067 Roo.form.Action.prototype = {
7069 failureType : undefined,
7070 response : undefined,
7074 run : function(options){
7079 success : function(response){
7084 handleResponse : function(response){
7088 // default connection failure
7089 failure : function(response){
7091 this.response = response;
7092 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7093 this.form.afterAction(this, false);
7096 processResponse : function(response){
7097 this.response = response;
7098 if(!response.responseText){
7101 this.result = this.handleResponse(response);
7105 // utility functions used internally
7106 getUrl : function(appendParams){
7107 var url = this.options.url || this.form.url || this.form.el.dom.action;
7109 var p = this.getParams();
7111 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7117 getMethod : function(){
7118 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7121 getParams : function(){
7122 var bp = this.form.baseParams;
7123 var p = this.options.params;
7125 if(typeof p == "object"){
7126 p = Roo.urlEncode(Roo.applyIf(p, bp));
7127 }else if(typeof p == 'string' && bp){
7128 p += '&' + Roo.urlEncode(bp);
7131 p = Roo.urlEncode(bp);
7136 createCallback : function(){
7138 success: this.success,
7139 failure: this.failure,
7141 timeout: (this.form.timeout*1000),
7142 upload: this.form.fileUpload ? this.success : undefined
7147 Roo.form.Action.Submit = function(form, options){
7148 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7151 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7154 haveProgress : false,
7155 uploadComplete : false,
7157 // uploadProgress indicator.
7158 uploadProgress : function()
7160 if (!this.form.progressUrl) {
7164 if (!this.haveProgress) {
7165 Roo.MessageBox.progress("Uploading", "Uploading");
7167 if (this.uploadComplete) {
7168 Roo.MessageBox.hide();
7172 this.haveProgress = true;
7174 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7176 var c = new Roo.data.Connection();
7178 url : this.form.progressUrl,
7183 success : function(req){
7184 //console.log(data);
7188 rdata = Roo.decode(req.responseText)
7190 Roo.log("Invalid data from server..");
7194 if (!rdata || !rdata.success) {
7196 Roo.MessageBox.alert(Roo.encode(rdata));
7199 var data = rdata.data;
7201 if (this.uploadComplete) {
7202 Roo.MessageBox.hide();
7207 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7208 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7211 this.uploadProgress.defer(2000,this);
7214 failure: function(data) {
7215 Roo.log('progress url failed ');
7226 // run get Values on the form, so it syncs any secondary forms.
7227 this.form.getValues();
7229 var o = this.options;
7230 var method = this.getMethod();
7231 var isPost = method == 'POST';
7232 if(o.clientValidation === false || this.form.isValid()){
7234 if (this.form.progressUrl) {
7235 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7236 (new Date() * 1) + '' + Math.random());
7241 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7242 form:this.form.el.dom,
7243 url:this.getUrl(!isPost),
7245 params:isPost ? this.getParams() : null,
7246 isUpload: this.form.fileUpload
7249 this.uploadProgress();
7251 }else if (o.clientValidation !== false){ // client validation failed
7252 this.failureType = Roo.form.Action.CLIENT_INVALID;
7253 this.form.afterAction(this, false);
7257 success : function(response)
7259 this.uploadComplete= true;
7260 if (this.haveProgress) {
7261 Roo.MessageBox.hide();
7265 var result = this.processResponse(response);
7266 if(result === true || result.success){
7267 this.form.afterAction(this, true);
7271 this.form.markInvalid(result.errors);
7272 this.failureType = Roo.form.Action.SERVER_INVALID;
7274 this.form.afterAction(this, false);
7276 failure : function(response)
7278 this.uploadComplete= true;
7279 if (this.haveProgress) {
7280 Roo.MessageBox.hide();
7283 this.response = response;
7284 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7285 this.form.afterAction(this, false);
7288 handleResponse : function(response){
7289 if(this.form.errorReader){
7290 var rs = this.form.errorReader.read(response);
7293 for(var i = 0, len = rs.records.length; i < len; i++) {
7294 var r = rs.records[i];
7298 if(errors.length < 1){
7302 success : rs.success,
7308 ret = Roo.decode(response.responseText);
7312 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7322 Roo.form.Action.Load = function(form, options){
7323 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7324 this.reader = this.form.reader;
7327 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7332 Roo.Ajax.request(Roo.apply(
7333 this.createCallback(), {
7334 method:this.getMethod(),
7335 url:this.getUrl(false),
7336 params:this.getParams()
7340 success : function(response){
7342 var result = this.processResponse(response);
7343 if(result === true || !result.success || !result.data){
7344 this.failureType = Roo.form.Action.LOAD_FAILURE;
7345 this.form.afterAction(this, false);
7348 this.form.clearInvalid();
7349 this.form.setValues(result.data);
7350 this.form.afterAction(this, true);
7353 handleResponse : function(response){
7354 if(this.form.reader){
7355 var rs = this.form.reader.read(response);
7356 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7358 success : rs.success,
7362 return Roo.decode(response.responseText);
7366 Roo.form.Action.ACTION_TYPES = {
7367 'load' : Roo.form.Action.Load,
7368 'submit' : Roo.form.Action.Submit
7377 * @class Roo.bootstrap.Form
7378 * @extends Roo.bootstrap.Component
7379 * Bootstrap Form class
7380 * @cfg {String} method GET | POST (default POST)
7381 * @cfg {String} labelAlign top | left (default top)
7382 * @cfg {String} align left | right - for navbars
7383 * @cfg {Boolean} loadMask load mask when submit (default true)
7388 * @param {Object} config The config object
7392 Roo.bootstrap.Form = function(config){
7393 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7396 * @event clientvalidation
7397 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7398 * @param {Form} this
7399 * @param {Boolean} valid true if the form has passed client-side validation
7401 clientvalidation: true,
7403 * @event beforeaction
7404 * Fires before any action is performed. Return false to cancel the action.
7405 * @param {Form} this
7406 * @param {Action} action The action to be performed
7410 * @event actionfailed
7411 * Fires when an action fails.
7412 * @param {Form} this
7413 * @param {Action} action The action that failed
7415 actionfailed : true,
7417 * @event actioncomplete
7418 * Fires when an action is completed.
7419 * @param {Form} this
7420 * @param {Action} action The action that completed
7422 actioncomplete : true
7427 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7430 * @cfg {String} method
7431 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7436 * The URL to use for form actions if one isn't supplied in the action options.
7439 * @cfg {Boolean} fileUpload
7440 * Set to true if this form is a file upload.
7444 * @cfg {Object} baseParams
7445 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7449 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7453 * @cfg {Sting} align (left|right) for navbar forms
7458 activeAction : null,
7461 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7462 * element by passing it or its id or mask the form itself by passing in true.
7465 waitMsgTarget : false,
7469 getAutoCreate : function(){
7473 method : this.method || 'POST',
7474 id : this.id || Roo.id(),
7477 if (this.parent().xtype.match(/^Nav/)) {
7478 cfg.cls = 'navbar-form navbar-' + this.align;
7482 if (this.labelAlign == 'left' ) {
7483 cfg.cls += ' form-horizontal';
7489 initEvents : function()
7491 this.el.on('submit', this.onSubmit, this);
7492 // this was added as random key presses on the form where triggering form submit.
7493 this.el.on('keypress', function(e) {
7494 if (e.getCharCode() != 13) {
7497 // we might need to allow it for textareas.. and some other items.
7498 // check e.getTarget().
7500 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7504 Roo.log("keypress blocked");
7512 onSubmit : function(e){
7517 * Returns true if client-side validation on the form is successful.
7520 isValid : function(){
7521 var items = this.getItems();
7523 items.each(function(f){
7532 * Returns true if any fields in this form have changed since their original load.
7535 isDirty : function(){
7537 var items = this.getItems();
7538 items.each(function(f){
7548 * Performs a predefined action (submit or load) or custom actions you define on this form.
7549 * @param {String} actionName The name of the action type
7550 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7551 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7552 * accept other config options):
7554 Property Type Description
7555 ---------------- --------------- ----------------------------------------------------------------------------------
7556 url String The url for the action (defaults to the form's url)
7557 method String The form method to use (defaults to the form's method, or POST if not defined)
7558 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7559 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7560 validate the form on the client (defaults to false)
7562 * @return {BasicForm} this
7564 doAction : function(action, options){
7565 if(typeof action == 'string'){
7566 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7568 if(this.fireEvent('beforeaction', this, action) !== false){
7569 this.beforeAction(action);
7570 action.run.defer(100, action);
7576 beforeAction : function(action){
7577 var o = action.options;
7580 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7582 // not really supported yet.. ??
7584 //if(this.waitMsgTarget === true){
7585 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7586 //}else if(this.waitMsgTarget){
7587 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7588 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7590 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7596 afterAction : function(action, success){
7597 this.activeAction = null;
7598 var o = action.options;
7600 //if(this.waitMsgTarget === true){
7602 //}else if(this.waitMsgTarget){
7603 // this.waitMsgTarget.unmask();
7605 // Roo.MessageBox.updateProgress(1);
7606 // Roo.MessageBox.hide();
7613 Roo.callback(o.success, o.scope, [this, action]);
7614 this.fireEvent('actioncomplete', this, action);
7618 // failure condition..
7619 // we have a scenario where updates need confirming.
7620 // eg. if a locking scenario exists..
7621 // we look for { errors : { needs_confirm : true }} in the response.
7623 (typeof(action.result) != 'undefined') &&
7624 (typeof(action.result.errors) != 'undefined') &&
7625 (typeof(action.result.errors.needs_confirm) != 'undefined')
7628 Roo.log("not supported yet");
7631 Roo.MessageBox.confirm(
7632 "Change requires confirmation",
7633 action.result.errorMsg,
7638 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7648 Roo.callback(o.failure, o.scope, [this, action]);
7649 // show an error message if no failed handler is set..
7650 if (!this.hasListener('actionfailed')) {
7651 Roo.log("need to add dialog support");
7653 Roo.MessageBox.alert("Error",
7654 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7655 action.result.errorMsg :
7656 "Saving Failed, please check your entries or try again"
7661 this.fireEvent('actionfailed', this, action);
7666 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7667 * @param {String} id The value to search for
7670 findField : function(id){
7671 var items = this.getItems();
7672 var field = items.get(id);
7674 items.each(function(f){
7675 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7682 return field || null;
7685 * Mark fields in this form invalid in bulk.
7686 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7687 * @return {BasicForm} this
7689 markInvalid : function(errors){
7690 if(errors instanceof Array){
7691 for(var i = 0, len = errors.length; i < len; i++){
7692 var fieldError = errors[i];
7693 var f = this.findField(fieldError.id);
7695 f.markInvalid(fieldError.msg);
7701 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7702 field.markInvalid(errors[id]);
7706 //Roo.each(this.childForms || [], function (f) {
7707 // f.markInvalid(errors);
7714 * Set values for fields in this form in bulk.
7715 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7716 * @return {BasicForm} this
7718 setValues : function(values){
7719 if(values instanceof Array){ // array of objects
7720 for(var i = 0, len = values.length; i < len; i++){
7722 var f = this.findField(v.id);
7724 f.setValue(v.value);
7725 if(this.trackResetOnLoad){
7726 f.originalValue = f.getValue();
7730 }else{ // object hash
7733 if(typeof values[id] != 'function' && (field = this.findField(id))){
7735 if (field.setFromData &&
7737 field.displayField &&
7738 // combos' with local stores can
7739 // be queried via setValue()
7740 // to set their value..
7741 (field.store && !field.store.isLocal)
7745 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7746 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7747 field.setFromData(sd);
7750 field.setValue(values[id]);
7754 if(this.trackResetOnLoad){
7755 field.originalValue = field.getValue();
7761 //Roo.each(this.childForms || [], function (f) {
7762 // f.setValues(values);
7769 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7770 * they are returned as an array.
7771 * @param {Boolean} asString
7774 getValues : function(asString){
7775 //if (this.childForms) {
7776 // copy values from the child forms
7777 // Roo.each(this.childForms, function (f) {
7778 // this.setValues(f.getValues());
7784 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7785 if(asString === true){
7788 return Roo.urlDecode(fs);
7792 * Returns the fields in this form as an object with key/value pairs.
7793 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7796 getFieldValues : function(with_hidden)
7798 var items = this.getItems();
7800 items.each(function(f){
7804 var v = f.getValue();
7805 if (f.inputType =='radio') {
7806 if (typeof(ret[f.getName()]) == 'undefined') {
7807 ret[f.getName()] = ''; // empty..
7810 if (!f.el.dom.checked) {
7818 // not sure if this supported any more..
7819 if ((typeof(v) == 'object') && f.getRawValue) {
7820 v = f.getRawValue() ; // dates..
7822 // combo boxes where name != hiddenName...
7823 if (f.name != f.getName()) {
7824 ret[f.name] = f.getRawValue();
7826 ret[f.getName()] = v;
7833 * Clears all invalid messages in this form.
7834 * @return {BasicForm} this
7836 clearInvalid : function(){
7837 var items = this.getItems();
7839 items.each(function(f){
7850 * @return {BasicForm} this
7853 var items = this.getItems();
7854 items.each(function(f){
7858 Roo.each(this.childForms || [], function (f) {
7865 getItems : function()
7867 var r=new Roo.util.MixedCollection(false, function(o){
7868 return o.id || (o.id = Roo.id());
7870 var iter = function(el) {
7877 Roo.each(el.items,function(e) {
7897 * Ext JS Library 1.1.1
7898 * Copyright(c) 2006-2007, Ext JS, LLC.
7900 * Originally Released Under LGPL - original licence link has changed is not relivant.
7903 * <script type="text/javascript">
7906 * @class Roo.form.VTypes
7907 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7910 Roo.form.VTypes = function(){
7911 // closure these in so they are only created once.
7912 var alpha = /^[a-zA-Z_]+$/;
7913 var alphanum = /^[a-zA-Z0-9_]+$/;
7914 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7915 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7917 // All these messages and functions are configurable
7920 * The function used to validate email addresses
7921 * @param {String} value The email address
7923 'email' : function(v){
7924 return email.test(v);
7927 * The error text to display when the email validation function returns false
7930 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7932 * The keystroke filter mask to be applied on email input
7935 'emailMask' : /[a-z0-9_\.\-@]/i,
7938 * The function used to validate URLs
7939 * @param {String} value The URL
7941 'url' : function(v){
7945 * The error text to display when the url validation function returns false
7948 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7951 * The function used to validate alpha values
7952 * @param {String} value The value
7954 'alpha' : function(v){
7955 return alpha.test(v);
7958 * The error text to display when the alpha validation function returns false
7961 'alphaText' : 'This field should only contain letters and _',
7963 * The keystroke filter mask to be applied on alpha input
7966 'alphaMask' : /[a-z_]/i,
7969 * The function used to validate alphanumeric values
7970 * @param {String} value The value
7972 'alphanum' : function(v){
7973 return alphanum.test(v);
7976 * The error text to display when the alphanumeric validation function returns false
7979 'alphanumText' : 'This field should only contain letters, numbers and _',
7981 * The keystroke filter mask to be applied on alphanumeric input
7984 'alphanumMask' : /[a-z0-9_]/i
7994 * @class Roo.bootstrap.Input
7995 * @extends Roo.bootstrap.Component
7996 * Bootstrap Input class
7997 * @cfg {Boolean} disabled is it disabled
7998 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7999 * @cfg {String} name name of the input
8000 * @cfg {string} fieldLabel - the label associated
8001 * @cfg {string} placeholder - placeholder to put in text.
8002 * @cfg {string} before - input group add on before
8003 * @cfg {string} after - input group add on after
8004 * @cfg {string} size - (lg|sm) or leave empty..
8005 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8006 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8007 * @cfg {Number} md colspan out of 12 for computer-sized screens
8008 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8009 * @cfg {string} value default value of the input
8010 * @cfg {Number} labelWidth set the width of label (0-12)
8011 * @cfg {String} labelAlign (top|left)
8012 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8013 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8015 * @cfg {String} align (left|center|right) Default left
8016 * @cfg {Boolean} forceFeedback (true|false) Default false
8022 * Create a new Input
8023 * @param {Object} config The config object
8026 Roo.bootstrap.Input = function(config){
8027 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8032 * Fires when this field receives input focus.
8033 * @param {Roo.form.Field} this
8038 * Fires when this field loses input focus.
8039 * @param {Roo.form.Field} this
8044 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8045 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8046 * @param {Roo.form.Field} this
8047 * @param {Roo.EventObject} e The event object
8052 * Fires just before the field blurs if the field value has changed.
8053 * @param {Roo.form.Field} this
8054 * @param {Mixed} newValue The new value
8055 * @param {Mixed} oldValue The original value
8060 * Fires after the field has been marked as invalid.
8061 * @param {Roo.form.Field} this
8062 * @param {String} msg The validation message
8067 * Fires after the field has been validated with no errors.
8068 * @param {Roo.form.Field} this
8073 * Fires after the key up
8074 * @param {Roo.form.Field} this
8075 * @param {Roo.EventObject} e The event Object
8081 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8083 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8084 automatic validation (defaults to "keyup").
8086 validationEvent : "keyup",
8088 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8090 validateOnBlur : true,
8092 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8094 validationDelay : 250,
8096 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8098 focusClass : "x-form-focus", // not needed???
8102 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8104 invalidClass : "has-warning",
8107 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8109 validClass : "has-success",
8112 * @cfg {Boolean} hasFeedback (true|false) default true
8117 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8119 invalidFeedbackClass : "glyphicon-warning-sign",
8122 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8124 validFeedbackClass : "glyphicon-ok",
8127 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8129 selectOnFocus : false,
8132 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8136 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8141 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8143 disableKeyFilter : false,
8146 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8150 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8154 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8156 blankText : "This field is required",
8159 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8163 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8165 maxLength : Number.MAX_VALUE,
8167 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8169 minLengthText : "The minimum length for this field is {0}",
8171 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8173 maxLengthText : "The maximum length for this field is {0}",
8177 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8178 * If available, this function will be called only after the basic validators all return true, and will be passed the
8179 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8183 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8184 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8185 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8189 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8193 autocomplete: false,
8212 formatedValue : false,
8213 forceFeedback : false,
8215 parentLabelAlign : function()
8218 while (parent.parent()) {
8219 parent = parent.parent();
8220 if (typeof(parent.labelAlign) !='undefined') {
8221 return parent.labelAlign;
8228 getAutoCreate : function(){
8230 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8238 if(this.inputType != 'hidden'){
8239 cfg.cls = 'form-group' //input-group
8245 type : this.inputType,
8247 cls : 'form-control',
8248 placeholder : this.placeholder || '',
8249 autocomplete : this.autocomplete || 'new-password'
8254 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8257 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8258 input.maxLength = this.maxLength;
8261 if (this.disabled) {
8262 input.disabled=true;
8265 if (this.readOnly) {
8266 input.readonly=true;
8270 input.name = this.name;
8273 input.cls += ' input-' + this.size;
8276 ['xs','sm','md','lg'].map(function(size){
8277 if (settings[size]) {
8278 cfg.cls += ' col-' + size + '-' + settings[size];
8282 var inputblock = input;
8286 cls: 'glyphicon form-control-feedback'
8289 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8292 cls : 'has-feedback',
8300 if (this.before || this.after) {
8303 cls : 'input-group',
8307 if (this.before && typeof(this.before) == 'string') {
8309 inputblock.cn.push({
8311 cls : 'roo-input-before input-group-addon',
8315 if (this.before && typeof(this.before) == 'object') {
8316 this.before = Roo.factory(this.before);
8318 inputblock.cn.push({
8320 cls : 'roo-input-before input-group-' +
8321 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8325 inputblock.cn.push(input);
8327 if (this.after && typeof(this.after) == 'string') {
8328 inputblock.cn.push({
8330 cls : 'roo-input-after input-group-addon',
8334 if (this.after && typeof(this.after) == 'object') {
8335 this.after = Roo.factory(this.after);
8337 inputblock.cn.push({
8339 cls : 'roo-input-after input-group-' +
8340 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8344 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8345 inputblock.cls += ' has-feedback';
8346 inputblock.cn.push(feedback);
8350 if (align ==='left' && this.fieldLabel.length) {
8357 cls : 'control-label col-sm-' + this.labelWidth,
8358 html : this.fieldLabel
8362 cls : "col-sm-" + (12 - this.labelWidth),
8369 } else if ( this.fieldLabel.length) {
8375 //cls : 'input-group-addon',
8376 html : this.fieldLabel
8395 if (this.parentType === 'Navbar' && this.parent().bar) {
8396 cfg.cls += ' navbar-form';
8398 if (this.parentType === 'NavGroup') {
8399 cfg.cls += ' navbar-form';
8406 * return the real input element.
8408 inputEl: function ()
8410 return this.el.select('input.form-control',true).first();
8413 tooltipEl : function()
8415 return this.inputEl();
8418 setDisabled : function(v)
8420 var i = this.inputEl().dom;
8422 i.removeAttribute('disabled');
8426 i.setAttribute('disabled','true');
8428 initEvents : function()
8431 this.inputEl().on("keydown" , this.fireKey, this);
8432 this.inputEl().on("focus", this.onFocus, this);
8433 this.inputEl().on("blur", this.onBlur, this);
8435 this.inputEl().relayEvent('keyup', this);
8437 // reference to original value for reset
8438 this.originalValue = this.getValue();
8439 //Roo.form.TextField.superclass.initEvents.call(this);
8440 if(this.validationEvent == 'keyup'){
8441 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8442 this.inputEl().on('keyup', this.filterValidation, this);
8444 else if(this.validationEvent !== false){
8445 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8448 if(this.selectOnFocus){
8449 this.on("focus", this.preFocus, this);
8452 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8453 this.inputEl().on("keypress", this.filterKeys, this);
8456 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8457 this.el.on("click", this.autoSize, this);
8460 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8461 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8464 if (typeof(this.before) == 'object') {
8465 this.before.render(this.el.select('.roo-input-before',true).first());
8467 if (typeof(this.after) == 'object') {
8468 this.after.render(this.el.select('.roo-input-after',true).first());
8473 filterValidation : function(e){
8474 if(!e.isNavKeyPress()){
8475 this.validationTask.delay(this.validationDelay);
8479 * Validates the field value
8480 * @return {Boolean} True if the value is valid, else false
8482 validate : function(){
8483 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8484 if(this.disabled || this.validateValue(this.getRawValue())){
8495 * Validates a value according to the field's validation rules and marks the field as invalid
8496 * if the validation fails
8497 * @param {Mixed} value The value to validate
8498 * @return {Boolean} True if the value is valid, else false
8500 validateValue : function(value){
8501 if(value.length < 1) { // if it's blank
8502 if(this.allowBlank){
8508 if(value.length < this.minLength){
8511 if(value.length > this.maxLength){
8515 var vt = Roo.form.VTypes;
8516 if(!vt[this.vtype](value, this)){
8520 if(typeof this.validator == "function"){
8521 var msg = this.validator(value);
8527 if(this.regex && !this.regex.test(value)){
8537 fireKey : function(e){
8538 //Roo.log('field ' + e.getKey());
8539 if(e.isNavKeyPress()){
8540 this.fireEvent("specialkey", this, e);
8543 focus : function (selectText){
8545 this.inputEl().focus();
8546 if(selectText === true){
8547 this.inputEl().dom.select();
8553 onFocus : function(){
8554 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8555 // this.el.addClass(this.focusClass);
8558 this.hasFocus = true;
8559 this.startValue = this.getValue();
8560 this.fireEvent("focus", this);
8564 beforeBlur : Roo.emptyFn,
8568 onBlur : function(){
8570 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8571 //this.el.removeClass(this.focusClass);
8573 this.hasFocus = false;
8574 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8577 var v = this.getValue();
8578 if(String(v) !== String(this.startValue)){
8579 this.fireEvent('change', this, v, this.startValue);
8581 this.fireEvent("blur", this);
8585 * Resets the current field value to the originally loaded value and clears any validation messages
8588 this.setValue(this.originalValue);
8592 * Returns the name of the field
8593 * @return {Mixed} name The name field
8595 getName: function(){
8599 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8600 * @return {Mixed} value The field value
8602 getValue : function(){
8604 var v = this.inputEl().getValue();
8609 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8610 * @return {Mixed} value The field value
8612 getRawValue : function(){
8613 var v = this.inputEl().getValue();
8619 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8620 * @param {Mixed} value The value to set
8622 setRawValue : function(v){
8623 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8626 selectText : function(start, end){
8627 var v = this.getRawValue();
8629 start = start === undefined ? 0 : start;
8630 end = end === undefined ? v.length : end;
8631 var d = this.inputEl().dom;
8632 if(d.setSelectionRange){
8633 d.setSelectionRange(start, end);
8634 }else if(d.createTextRange){
8635 var range = d.createTextRange();
8636 range.moveStart("character", start);
8637 range.moveEnd("character", v.length-end);
8644 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8645 * @param {Mixed} value The value to set
8647 setValue : function(v){
8650 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8656 processValue : function(value){
8657 if(this.stripCharsRe){
8658 var newValue = value.replace(this.stripCharsRe, '');
8659 if(newValue !== value){
8660 this.setRawValue(newValue);
8667 preFocus : function(){
8669 if(this.selectOnFocus){
8670 this.inputEl().dom.select();
8673 filterKeys : function(e){
8675 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8678 var c = e.getCharCode(), cc = String.fromCharCode(c);
8679 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8682 if(!this.maskRe.test(cc)){
8687 * Clear any invalid styles/messages for this field
8689 clearInvalid : function(){
8691 if(!this.el || this.preventMark){ // not rendered
8695 var label = this.el.select('label', true).first();
8696 var icon = this.el.select('i.fa-star', true).first();
8702 this.el.removeClass(this.invalidClass);
8704 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8706 var feedback = this.el.select('.form-control-feedback', true).first();
8709 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8714 this.fireEvent('valid', this);
8718 * Mark this field as valid
8720 markValid : function()
8722 if(!this.el || this.preventMark){ // not rendered
8726 this.el.removeClass([this.invalidClass, this.validClass]);
8728 var feedback = this.el.select('.form-control-feedback', true).first();
8731 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8734 if(this.disabled || this.allowBlank){
8738 var formGroup = this.el.findParent('.form-group', false, true);
8742 var label = formGroup.select('label', true).first();
8743 var icon = formGroup.select('i.fa-star', true).first();
8750 this.el.addClass(this.validClass);
8752 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8754 var feedback = this.el.select('.form-control-feedback', true).first();
8757 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8758 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8763 this.fireEvent('valid', this);
8767 * Mark this field as invalid
8768 * @param {String} msg The validation message
8770 markInvalid : function(msg)
8772 if(!this.el || this.preventMark){ // not rendered
8776 this.el.removeClass([this.invalidClass, this.validClass]);
8778 var feedback = this.el.select('.form-control-feedback', true).first();
8781 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8784 if(this.disabled || this.allowBlank){
8788 var formGroup = this.el.findParent('.form-group', false, true);
8791 var label = formGroup.select('label', true).first();
8792 var icon = formGroup.select('i.fa-star', true).first();
8794 if(!this.getValue().length && label && !icon){
8795 this.el.findParent('.form-group', false, true).createChild({
8797 cls : 'text-danger fa fa-lg fa-star',
8798 tooltip : 'This field is required',
8799 style : 'margin-right:5px;'
8805 this.el.addClass(this.invalidClass);
8807 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8809 var feedback = this.el.select('.form-control-feedback', true).first();
8812 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8814 if(this.getValue().length || this.forceFeedback){
8815 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8822 this.fireEvent('invalid', this, msg);
8825 SafariOnKeyDown : function(event)
8827 // this is a workaround for a password hang bug on chrome/ webkit.
8829 var isSelectAll = false;
8831 if(this.inputEl().dom.selectionEnd > 0){
8832 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8834 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8835 event.preventDefault();
8840 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8842 event.preventDefault();
8843 // this is very hacky as keydown always get's upper case.
8845 var cc = String.fromCharCode(event.getCharCode());
8846 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8850 adjustWidth : function(tag, w){
8851 tag = tag.toLowerCase();
8852 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8853 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8857 if(tag == 'textarea'){
8860 }else if(Roo.isOpera){
8864 if(tag == 'textarea'){
8883 * @class Roo.bootstrap.TextArea
8884 * @extends Roo.bootstrap.Input
8885 * Bootstrap TextArea class
8886 * @cfg {Number} cols Specifies the visible width of a text area
8887 * @cfg {Number} rows Specifies the visible number of lines in a text area
8888 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8889 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8890 * @cfg {string} html text
8893 * Create a new TextArea
8894 * @param {Object} config The config object
8897 Roo.bootstrap.TextArea = function(config){
8898 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8902 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8912 getAutoCreate : function(){
8914 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8925 value : this.value || '',
8926 html: this.html || '',
8927 cls : 'form-control',
8928 placeholder : this.placeholder || ''
8932 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8933 input.maxLength = this.maxLength;
8937 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8941 input.cols = this.cols;
8944 if (this.readOnly) {
8945 input.readonly = true;
8949 input.name = this.name;
8953 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8957 ['xs','sm','md','lg'].map(function(size){
8958 if (settings[size]) {
8959 cfg.cls += ' col-' + size + '-' + settings[size];
8963 var inputblock = input;
8965 if(this.hasFeedback && !this.allowBlank){
8969 cls: 'glyphicon form-control-feedback'
8973 cls : 'has-feedback',
8982 if (this.before || this.after) {
8985 cls : 'input-group',
8989 inputblock.cn.push({
8991 cls : 'input-group-addon',
8996 inputblock.cn.push(input);
8998 if(this.hasFeedback && !this.allowBlank){
8999 inputblock.cls += ' has-feedback';
9000 inputblock.cn.push(feedback);
9004 inputblock.cn.push({
9006 cls : 'input-group-addon',
9013 if (align ==='left' && this.fieldLabel.length) {
9014 // Roo.log("left and has label");
9020 cls : 'control-label col-sm-' + this.labelWidth,
9021 html : this.fieldLabel
9025 cls : "col-sm-" + (12 - this.labelWidth),
9032 } else if ( this.fieldLabel.length) {
9033 // Roo.log(" label");
9038 //cls : 'input-group-addon',
9039 html : this.fieldLabel
9049 // Roo.log(" no label && no align");
9059 if (this.disabled) {
9060 input.disabled=true;
9067 * return the real textarea element.
9069 inputEl: function ()
9071 return this.el.select('textarea.form-control',true).first();
9075 * Clear any invalid styles/messages for this field
9077 clearInvalid : function()
9080 if(!this.el || this.preventMark){ // not rendered
9084 var label = this.el.select('label', true).first();
9085 var icon = this.el.select('i.fa-star', true).first();
9091 this.el.removeClass(this.invalidClass);
9093 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9095 var feedback = this.el.select('.form-control-feedback', true).first();
9098 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9103 this.fireEvent('valid', this);
9107 * Mark this field as valid
9109 markValid : function()
9111 if(!this.el || this.preventMark){ // not rendered
9115 this.el.removeClass([this.invalidClass, this.validClass]);
9117 var feedback = this.el.select('.form-control-feedback', true).first();
9120 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9123 if(this.disabled || this.allowBlank){
9127 var label = this.el.select('label', true).first();
9128 var icon = this.el.select('i.fa-star', true).first();
9134 this.el.addClass(this.validClass);
9136 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9138 var feedback = this.el.select('.form-control-feedback', true).first();
9141 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9142 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9147 this.fireEvent('valid', this);
9151 * Mark this field as invalid
9152 * @param {String} msg The validation message
9154 markInvalid : function(msg)
9156 if(!this.el || this.preventMark){ // not rendered
9160 this.el.removeClass([this.invalidClass, this.validClass]);
9162 var feedback = this.el.select('.form-control-feedback', true).first();
9165 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9168 if(this.disabled || this.allowBlank){
9172 var label = this.el.select('label', true).first();
9173 var icon = this.el.select('i.fa-star', true).first();
9175 if(!this.getValue().length && label && !icon){
9176 this.el.createChild({
9178 cls : 'text-danger fa fa-lg fa-star',
9179 tooltip : 'This field is required',
9180 style : 'margin-right:5px;'
9184 this.el.addClass(this.invalidClass);
9186 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9188 var feedback = this.el.select('.form-control-feedback', true).first();
9191 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9193 if(this.getValue().length || this.forceFeedback){
9194 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9201 this.fireEvent('invalid', this, msg);
9209 * trigger field - base class for combo..
9214 * @class Roo.bootstrap.TriggerField
9215 * @extends Roo.bootstrap.Input
9216 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9217 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9218 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9219 * for which you can provide a custom implementation. For example:
9221 var trigger = new Roo.bootstrap.TriggerField();
9222 trigger.onTriggerClick = myTriggerFn;
9223 trigger.applyTo('my-field');
9226 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9227 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9228 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9229 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9230 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9233 * Create a new TriggerField.
9234 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9235 * to the base TextField)
9237 Roo.bootstrap.TriggerField = function(config){
9238 this.mimicing = false;
9239 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9242 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9244 * @cfg {String} triggerClass A CSS class to apply to the trigger
9247 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9252 * @cfg {Boolean} removable (true|false) special filter default false
9256 /** @cfg {Boolean} grow @hide */
9257 /** @cfg {Number} growMin @hide */
9258 /** @cfg {Number} growMax @hide */
9264 autoSize: Roo.emptyFn,
9271 actionMode : 'wrap',
9276 getAutoCreate : function(){
9278 var align = this.labelAlign || this.parentLabelAlign();
9283 cls: 'form-group' //input-group
9290 type : this.inputType,
9291 cls : 'form-control',
9292 autocomplete: 'new-password',
9293 placeholder : this.placeholder || ''
9297 input.name = this.name;
9300 input.cls += ' input-' + this.size;
9303 if (this.disabled) {
9304 input.disabled=true;
9307 var inputblock = input;
9309 if(this.hasFeedback && !this.allowBlank){
9313 cls: 'glyphicon form-control-feedback'
9316 if(this.removable && !this.editable && !this.tickable){
9318 cls : 'has-feedback',
9324 cls : 'roo-combo-removable-btn close'
9331 cls : 'has-feedback',
9340 if(this.removable && !this.editable && !this.tickable){
9342 cls : 'roo-removable',
9348 cls : 'roo-combo-removable-btn close'
9355 if (this.before || this.after) {
9358 cls : 'input-group',
9362 inputblock.cn.push({
9364 cls : 'input-group-addon',
9369 inputblock.cn.push(input);
9371 if(this.hasFeedback && !this.allowBlank){
9372 inputblock.cls += ' has-feedback';
9373 inputblock.cn.push(feedback);
9377 inputblock.cn.push({
9379 cls : 'input-group-addon',
9392 cls: 'form-hidden-field'
9406 cls: 'form-hidden-field'
9410 cls: 'roo-select2-choices',
9414 cls: 'roo-select2-search-field',
9427 cls: 'roo-select2-container input-group',
9432 // cls: 'typeahead typeahead-long dropdown-menu',
9433 // style: 'display:none'
9438 if(!this.multiple && this.showToggleBtn){
9444 if (this.caret != false) {
9447 cls: 'fa fa-' + this.caret
9454 cls : 'input-group-addon btn dropdown-toggle',
9459 cls: 'combobox-clear',
9473 combobox.cls += ' roo-select2-container-multi';
9476 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9478 // Roo.log("left and has label");
9484 cls : 'control-label col-sm-' + this.labelWidth,
9485 html : this.fieldLabel
9489 cls : "col-sm-" + (12 - this.labelWidth),
9496 } else if ( this.fieldLabel.length) {
9497 // Roo.log(" label");
9502 //cls : 'input-group-addon',
9503 html : this.fieldLabel
9513 // Roo.log(" no label && no align");
9520 ['xs','sm','md','lg'].map(function(size){
9521 if (settings[size]) {
9522 cfg.cls += ' col-' + size + '-' + settings[size];
9533 onResize : function(w, h){
9534 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9535 // if(typeof w == 'number'){
9536 // var x = w - this.trigger.getWidth();
9537 // this.inputEl().setWidth(this.adjustWidth('input', x));
9538 // this.trigger.setStyle('left', x+'px');
9543 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9546 getResizeEl : function(){
9547 return this.inputEl();
9551 getPositionEl : function(){
9552 return this.inputEl();
9556 alignErrorIcon : function(){
9557 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9561 initEvents : function(){
9565 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9566 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9567 if(!this.multiple && this.showToggleBtn){
9568 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9569 if(this.hideTrigger){
9570 this.trigger.setDisplayed(false);
9572 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9576 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9579 if(this.removable && !this.editable && !this.tickable){
9580 var close = this.closeTriggerEl();
9583 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9584 close.on('click', this.removeBtnClick, this, close);
9588 //this.trigger.addClassOnOver('x-form-trigger-over');
9589 //this.trigger.addClassOnClick('x-form-trigger-click');
9592 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9596 closeTriggerEl : function()
9598 var close = this.el.select('.roo-combo-removable-btn', true).first();
9599 return close ? close : false;
9602 removeBtnClick : function(e, h, el)
9606 if(this.fireEvent("remove", this) !== false){
9608 this.fireEvent("afterremove", this)
9612 createList : function()
9614 this.list = Roo.get(document.body).createChild({
9616 cls: 'typeahead typeahead-long dropdown-menu',
9617 style: 'display:none'
9620 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9625 initTrigger : function(){
9630 onDestroy : function(){
9632 this.trigger.removeAllListeners();
9633 // this.trigger.remove();
9636 // this.wrap.remove();
9638 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9642 onFocus : function(){
9643 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9646 this.wrap.addClass('x-trigger-wrap-focus');
9647 this.mimicing = true;
9648 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9649 if(this.monitorTab){
9650 this.el.on("keydown", this.checkTab, this);
9657 checkTab : function(e){
9658 if(e.getKey() == e.TAB){
9664 onBlur : function(){
9669 mimicBlur : function(e, t){
9671 if(!this.wrap.contains(t) && this.validateBlur()){
9678 triggerBlur : function(){
9679 this.mimicing = false;
9680 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9681 if(this.monitorTab){
9682 this.el.un("keydown", this.checkTab, this);
9684 //this.wrap.removeClass('x-trigger-wrap-focus');
9685 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9689 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9690 validateBlur : function(e, t){
9695 onDisable : function(){
9696 this.inputEl().dom.disabled = true;
9697 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9699 // this.wrap.addClass('x-item-disabled');
9704 onEnable : function(){
9705 this.inputEl().dom.disabled = false;
9706 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9708 // this.el.removeClass('x-item-disabled');
9713 onShow : function(){
9714 var ae = this.getActionEl();
9717 ae.dom.style.display = '';
9718 ae.dom.style.visibility = 'visible';
9724 onHide : function(){
9725 var ae = this.getActionEl();
9726 ae.dom.style.display = 'none';
9730 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9731 * by an implementing function.
9733 * @param {EventObject} e
9735 onTriggerClick : Roo.emptyFn
9739 * Ext JS Library 1.1.1
9740 * Copyright(c) 2006-2007, Ext JS, LLC.
9742 * Originally Released Under LGPL - original licence link has changed is not relivant.
9745 * <script type="text/javascript">
9750 * @class Roo.data.SortTypes
9752 * Defines the default sorting (casting?) comparison functions used when sorting data.
9754 Roo.data.SortTypes = {
9756 * Default sort that does nothing
9757 * @param {Mixed} s The value being converted
9758 * @return {Mixed} The comparison value
9765 * The regular expression used to strip tags
9769 stripTagsRE : /<\/?[^>]+>/gi,
9772 * Strips all HTML tags to sort on text only
9773 * @param {Mixed} s The value being converted
9774 * @return {String} The comparison value
9776 asText : function(s){
9777 return String(s).replace(this.stripTagsRE, "");
9781 * Strips all HTML tags to sort on text only - Case insensitive
9782 * @param {Mixed} s The value being converted
9783 * @return {String} The comparison value
9785 asUCText : function(s){
9786 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9790 * Case insensitive string
9791 * @param {Mixed} s The value being converted
9792 * @return {String} The comparison value
9794 asUCString : function(s) {
9795 return String(s).toUpperCase();
9800 * @param {Mixed} s The value being converted
9801 * @return {Number} The comparison value
9803 asDate : function(s) {
9807 if(s instanceof Date){
9810 return Date.parse(String(s));
9815 * @param {Mixed} s The value being converted
9816 * @return {Float} The comparison value
9818 asFloat : function(s) {
9819 var val = parseFloat(String(s).replace(/,/g, ""));
9828 * @param {Mixed} s The value being converted
9829 * @return {Number} The comparison value
9831 asInt : function(s) {
9832 var val = parseInt(String(s).replace(/,/g, ""));
9840 * Ext JS Library 1.1.1
9841 * Copyright(c) 2006-2007, Ext JS, LLC.
9843 * Originally Released Under LGPL - original licence link has changed is not relivant.
9846 * <script type="text/javascript">
9850 * @class Roo.data.Record
9851 * Instances of this class encapsulate both record <em>definition</em> information, and record
9852 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9853 * to access Records cached in an {@link Roo.data.Store} object.<br>
9855 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9856 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9859 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9861 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9862 * {@link #create}. The parameters are the same.
9863 * @param {Array} data An associative Array of data values keyed by the field name.
9864 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9865 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9866 * not specified an integer id is generated.
9868 Roo.data.Record = function(data, id){
9869 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9874 * Generate a constructor for a specific record layout.
9875 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9876 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9877 * Each field definition object may contain the following properties: <ul>
9878 * <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,
9879 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9880 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9881 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9882 * is being used, then this is a string containing the javascript expression to reference the data relative to
9883 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9884 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9885 * this may be omitted.</p></li>
9886 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9887 * <ul><li>auto (Default, implies no conversion)</li>
9892 * <li>date</li></ul></p></li>
9893 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9894 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9895 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9896 * by the Reader into an object that will be stored in the Record. It is passed the
9897 * following parameters:<ul>
9898 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9900 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9902 * <br>usage:<br><pre><code>
9903 var TopicRecord = Roo.data.Record.create(
9904 {name: 'title', mapping: 'topic_title'},
9905 {name: 'author', mapping: 'username'},
9906 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9907 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9908 {name: 'lastPoster', mapping: 'user2'},
9909 {name: 'excerpt', mapping: 'post_text'}
9912 var myNewRecord = new TopicRecord({
9913 title: 'Do my job please',
9916 lastPost: new Date(),
9917 lastPoster: 'Animal',
9918 excerpt: 'No way dude!'
9920 myStore.add(myNewRecord);
9925 Roo.data.Record.create = function(o){
9927 f.superclass.constructor.apply(this, arguments);
9929 Roo.extend(f, Roo.data.Record);
9930 var p = f.prototype;
9931 p.fields = new Roo.util.MixedCollection(false, function(field){
9934 for(var i = 0, len = o.length; i < len; i++){
9935 p.fields.add(new Roo.data.Field(o[i]));
9937 f.getField = function(name){
9938 return p.fields.get(name);
9943 Roo.data.Record.AUTO_ID = 1000;
9944 Roo.data.Record.EDIT = 'edit';
9945 Roo.data.Record.REJECT = 'reject';
9946 Roo.data.Record.COMMIT = 'commit';
9948 Roo.data.Record.prototype = {
9950 * Readonly flag - true if this record has been modified.
9959 join : function(store){
9964 * Set the named field to the specified value.
9965 * @param {String} name The name of the field to set.
9966 * @param {Object} value The value to set the field to.
9968 set : function(name, value){
9969 if(this.data[name] == value){
9976 if(typeof this.modified[name] == 'undefined'){
9977 this.modified[name] = this.data[name];
9979 this.data[name] = value;
9980 if(!this.editing && this.store){
9981 this.store.afterEdit(this);
9986 * Get the value of the named field.
9987 * @param {String} name The name of the field to get the value of.
9988 * @return {Object} The value of the field.
9990 get : function(name){
9991 return this.data[name];
9995 beginEdit : function(){
9996 this.editing = true;
10001 cancelEdit : function(){
10002 this.editing = false;
10003 delete this.modified;
10007 endEdit : function(){
10008 this.editing = false;
10009 if(this.dirty && this.store){
10010 this.store.afterEdit(this);
10015 * Usually called by the {@link Roo.data.Store} which owns the Record.
10016 * Rejects all changes made to the Record since either creation, or the last commit operation.
10017 * Modified fields are reverted to their original values.
10019 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10020 * of reject operations.
10022 reject : function(){
10023 var m = this.modified;
10025 if(typeof m[n] != "function"){
10026 this.data[n] = m[n];
10029 this.dirty = false;
10030 delete this.modified;
10031 this.editing = false;
10033 this.store.afterReject(this);
10038 * Usually called by the {@link Roo.data.Store} which owns the Record.
10039 * Commits all changes made to the Record since either creation, or the last commit operation.
10041 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10042 * of commit operations.
10044 commit : function(){
10045 this.dirty = false;
10046 delete this.modified;
10047 this.editing = false;
10049 this.store.afterCommit(this);
10054 hasError : function(){
10055 return this.error != null;
10059 clearError : function(){
10064 * Creates a copy of this record.
10065 * @param {String} id (optional) A new record id if you don't want to use this record's id
10068 copy : function(newId) {
10069 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10073 * Ext JS Library 1.1.1
10074 * Copyright(c) 2006-2007, Ext JS, LLC.
10076 * Originally Released Under LGPL - original licence link has changed is not relivant.
10079 * <script type="text/javascript">
10085 * @class Roo.data.Store
10086 * @extends Roo.util.Observable
10087 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10088 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10090 * 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
10091 * has no knowledge of the format of the data returned by the Proxy.<br>
10093 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10094 * instances from the data object. These records are cached and made available through accessor functions.
10096 * Creates a new Store.
10097 * @param {Object} config A config object containing the objects needed for the Store to access data,
10098 * and read the data into Records.
10100 Roo.data.Store = function(config){
10101 this.data = new Roo.util.MixedCollection(false);
10102 this.data.getKey = function(o){
10105 this.baseParams = {};
10107 this.paramNames = {
10112 "multisort" : "_multisort"
10115 if(config && config.data){
10116 this.inlineData = config.data;
10117 delete config.data;
10120 Roo.apply(this, config);
10122 if(this.reader){ // reader passed
10123 this.reader = Roo.factory(this.reader, Roo.data);
10124 this.reader.xmodule = this.xmodule || false;
10125 if(!this.recordType){
10126 this.recordType = this.reader.recordType;
10128 if(this.reader.onMetaChange){
10129 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10133 if(this.recordType){
10134 this.fields = this.recordType.prototype.fields;
10136 this.modified = [];
10140 * @event datachanged
10141 * Fires when the data cache has changed, and a widget which is using this Store
10142 * as a Record cache should refresh its view.
10143 * @param {Store} this
10145 datachanged : true,
10147 * @event metachange
10148 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10149 * @param {Store} this
10150 * @param {Object} meta The JSON metadata
10155 * Fires when Records have been added to the Store
10156 * @param {Store} this
10157 * @param {Roo.data.Record[]} records The array of Records added
10158 * @param {Number} index The index at which the record(s) were added
10163 * Fires when a Record has been removed from the Store
10164 * @param {Store} this
10165 * @param {Roo.data.Record} record The Record that was removed
10166 * @param {Number} index The index at which the record was removed
10171 * Fires when a Record has been updated
10172 * @param {Store} this
10173 * @param {Roo.data.Record} record The Record that was updated
10174 * @param {String} operation The update operation being performed. Value may be one of:
10176 Roo.data.Record.EDIT
10177 Roo.data.Record.REJECT
10178 Roo.data.Record.COMMIT
10184 * Fires when the data cache has been cleared.
10185 * @param {Store} this
10189 * @event beforeload
10190 * Fires before a request is made for a new data object. If the beforeload handler returns false
10191 * the load action will be canceled.
10192 * @param {Store} this
10193 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10197 * @event beforeloadadd
10198 * Fires after a new set of Records has been loaded.
10199 * @param {Store} this
10200 * @param {Roo.data.Record[]} records The Records that were loaded
10201 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10203 beforeloadadd : true,
10206 * Fires after a new set of Records has been loaded, before they are added to the store.
10207 * @param {Store} this
10208 * @param {Roo.data.Record[]} records The Records that were loaded
10209 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10210 * @params {Object} return from reader
10214 * @event loadexception
10215 * Fires if an exception occurs in the Proxy during loading.
10216 * Called with the signature of the Proxy's "loadexception" event.
10217 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10220 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10221 * @param {Object} load options
10222 * @param {Object} jsonData from your request (normally this contains the Exception)
10224 loadexception : true
10228 this.proxy = Roo.factory(this.proxy, Roo.data);
10229 this.proxy.xmodule = this.xmodule || false;
10230 this.relayEvents(this.proxy, ["loadexception"]);
10232 this.sortToggle = {};
10233 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10235 Roo.data.Store.superclass.constructor.call(this);
10237 if(this.inlineData){
10238 this.loadData(this.inlineData);
10239 delete this.inlineData;
10243 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10245 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10246 * without a remote query - used by combo/forms at present.
10250 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10253 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10256 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10257 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10260 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10261 * on any HTTP request
10264 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10267 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10271 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10272 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10274 remoteSort : false,
10277 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10278 * loaded or when a record is removed. (defaults to false).
10280 pruneModifiedRecords : false,
10283 lastOptions : null,
10286 * Add Records to the Store and fires the add event.
10287 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10289 add : function(records){
10290 records = [].concat(records);
10291 for(var i = 0, len = records.length; i < len; i++){
10292 records[i].join(this);
10294 var index = this.data.length;
10295 this.data.addAll(records);
10296 this.fireEvent("add", this, records, index);
10300 * Remove a Record from the Store and fires the remove event.
10301 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10303 remove : function(record){
10304 var index = this.data.indexOf(record);
10305 this.data.removeAt(index);
10306 if(this.pruneModifiedRecords){
10307 this.modified.remove(record);
10309 this.fireEvent("remove", this, record, index);
10313 * Remove all Records from the Store and fires the clear event.
10315 removeAll : function(){
10317 if(this.pruneModifiedRecords){
10318 this.modified = [];
10320 this.fireEvent("clear", this);
10324 * Inserts Records to the Store at the given index and fires the add event.
10325 * @param {Number} index The start index at which to insert the passed Records.
10326 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10328 insert : function(index, records){
10329 records = [].concat(records);
10330 for(var i = 0, len = records.length; i < len; i++){
10331 this.data.insert(index, records[i]);
10332 records[i].join(this);
10334 this.fireEvent("add", this, records, index);
10338 * Get the index within the cache of the passed Record.
10339 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10340 * @return {Number} The index of the passed Record. Returns -1 if not found.
10342 indexOf : function(record){
10343 return this.data.indexOf(record);
10347 * Get the index within the cache of the Record with the passed id.
10348 * @param {String} id The id of the Record to find.
10349 * @return {Number} The index of the Record. Returns -1 if not found.
10351 indexOfId : function(id){
10352 return this.data.indexOfKey(id);
10356 * Get the Record with the specified id.
10357 * @param {String} id The id of the Record to find.
10358 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10360 getById : function(id){
10361 return this.data.key(id);
10365 * Get the Record at the specified index.
10366 * @param {Number} index The index of the Record to find.
10367 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10369 getAt : function(index){
10370 return this.data.itemAt(index);
10374 * Returns a range of Records between specified indices.
10375 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10376 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10377 * @return {Roo.data.Record[]} An array of Records
10379 getRange : function(start, end){
10380 return this.data.getRange(start, end);
10384 storeOptions : function(o){
10385 o = Roo.apply({}, o);
10388 this.lastOptions = o;
10392 * Loads the Record cache from the configured Proxy using the configured Reader.
10394 * If using remote paging, then the first load call must specify the <em>start</em>
10395 * and <em>limit</em> properties in the options.params property to establish the initial
10396 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10398 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10399 * and this call will return before the new data has been loaded. Perform any post-processing
10400 * in a callback function, or in a "load" event handler.</strong>
10402 * @param {Object} options An object containing properties which control loading options:<ul>
10403 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10404 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10405 * passed the following arguments:<ul>
10406 * <li>r : Roo.data.Record[]</li>
10407 * <li>options: Options object from the load call</li>
10408 * <li>success: Boolean success indicator</li></ul></li>
10409 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10410 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10413 load : function(options){
10414 options = options || {};
10415 if(this.fireEvent("beforeload", this, options) !== false){
10416 this.storeOptions(options);
10417 var p = Roo.apply(options.params || {}, this.baseParams);
10418 // if meta was not loaded from remote source.. try requesting it.
10419 if (!this.reader.metaFromRemote) {
10420 p._requestMeta = 1;
10422 if(this.sortInfo && this.remoteSort){
10423 var pn = this.paramNames;
10424 p[pn["sort"]] = this.sortInfo.field;
10425 p[pn["dir"]] = this.sortInfo.direction;
10427 if (this.multiSort) {
10428 var pn = this.paramNames;
10429 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10432 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10437 * Reloads the Record cache from the configured Proxy using the configured Reader and
10438 * the options from the last load operation performed.
10439 * @param {Object} options (optional) An object containing properties which may override the options
10440 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10441 * the most recently used options are reused).
10443 reload : function(options){
10444 this.load(Roo.applyIf(options||{}, this.lastOptions));
10448 // Called as a callback by the Reader during a load operation.
10449 loadRecords : function(o, options, success){
10450 if(!o || success === false){
10451 if(success !== false){
10452 this.fireEvent("load", this, [], options, o);
10454 if(options.callback){
10455 options.callback.call(options.scope || this, [], options, false);
10459 // if data returned failure - throw an exception.
10460 if (o.success === false) {
10461 // show a message if no listener is registered.
10462 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10463 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10465 // loadmask wil be hooked into this..
10466 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10469 var r = o.records, t = o.totalRecords || r.length;
10471 this.fireEvent("beforeloadadd", this, r, options, o);
10473 if(!options || options.add !== true){
10474 if(this.pruneModifiedRecords){
10475 this.modified = [];
10477 for(var i = 0, len = r.length; i < len; i++){
10481 this.data = this.snapshot;
10482 delete this.snapshot;
10485 this.data.addAll(r);
10486 this.totalLength = t;
10488 this.fireEvent("datachanged", this);
10490 this.totalLength = Math.max(t, this.data.length+r.length);
10493 this.fireEvent("load", this, r, options, o);
10494 if(options.callback){
10495 options.callback.call(options.scope || this, r, options, true);
10501 * Loads data from a passed data block. A Reader which understands the format of the data
10502 * must have been configured in the constructor.
10503 * @param {Object} data The data block from which to read the Records. The format of the data expected
10504 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10505 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10507 loadData : function(o, append){
10508 var r = this.reader.readRecords(o);
10509 this.loadRecords(r, {add: append}, true);
10513 * Gets the number of cached records.
10515 * <em>If using paging, this may not be the total size of the dataset. If the data object
10516 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10517 * the data set size</em>
10519 getCount : function(){
10520 return this.data.length || 0;
10524 * Gets the total number of records in the dataset as returned by the server.
10526 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10527 * the dataset size</em>
10529 getTotalCount : function(){
10530 return this.totalLength || 0;
10534 * Returns the sort state of the Store as an object with two properties:
10536 field {String} The name of the field by which the Records are sorted
10537 direction {String} The sort order, "ASC" or "DESC"
10540 getSortState : function(){
10541 return this.sortInfo;
10545 applySort : function(){
10546 if(this.sortInfo && !this.remoteSort){
10547 var s = this.sortInfo, f = s.field;
10548 var st = this.fields.get(f).sortType;
10549 var fn = function(r1, r2){
10550 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10551 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10553 this.data.sort(s.direction, fn);
10554 if(this.snapshot && this.snapshot != this.data){
10555 this.snapshot.sort(s.direction, fn);
10561 * Sets the default sort column and order to be used by the next load operation.
10562 * @param {String} fieldName The name of the field to sort by.
10563 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10565 setDefaultSort : function(field, dir){
10566 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10570 * Sort the Records.
10571 * If remote sorting is used, the sort is performed on the server, and the cache is
10572 * reloaded. If local sorting is used, the cache is sorted internally.
10573 * @param {String} fieldName The name of the field to sort by.
10574 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10576 sort : function(fieldName, dir){
10577 var f = this.fields.get(fieldName);
10579 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10581 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10582 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10587 this.sortToggle[f.name] = dir;
10588 this.sortInfo = {field: f.name, direction: dir};
10589 if(!this.remoteSort){
10591 this.fireEvent("datachanged", this);
10593 this.load(this.lastOptions);
10598 * Calls the specified function for each of the Records in the cache.
10599 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10600 * Returning <em>false</em> aborts and exits the iteration.
10601 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10603 each : function(fn, scope){
10604 this.data.each(fn, scope);
10608 * Gets all records modified since the last commit. Modified records are persisted across load operations
10609 * (e.g., during paging).
10610 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10612 getModifiedRecords : function(){
10613 return this.modified;
10617 createFilterFn : function(property, value, anyMatch){
10618 if(!value.exec){ // not a regex
10619 value = String(value);
10620 if(value.length == 0){
10623 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10625 return function(r){
10626 return value.test(r.data[property]);
10631 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10632 * @param {String} property A field on your records
10633 * @param {Number} start The record index to start at (defaults to 0)
10634 * @param {Number} end The last record index to include (defaults to length - 1)
10635 * @return {Number} The sum
10637 sum : function(property, start, end){
10638 var rs = this.data.items, v = 0;
10639 start = start || 0;
10640 end = (end || end === 0) ? end : rs.length-1;
10642 for(var i = start; i <= end; i++){
10643 v += (rs[i].data[property] || 0);
10649 * Filter the records by a specified property.
10650 * @param {String} field A field on your records
10651 * @param {String/RegExp} value Either a string that the field
10652 * should start with or a RegExp to test against the field
10653 * @param {Boolean} anyMatch True to match any part not just the beginning
10655 filter : function(property, value, anyMatch){
10656 var fn = this.createFilterFn(property, value, anyMatch);
10657 return fn ? this.filterBy(fn) : this.clearFilter();
10661 * Filter by a function. The specified function will be called with each
10662 * record in this data source. If the function returns true the record is included,
10663 * otherwise it is filtered.
10664 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10665 * @param {Object} scope (optional) The scope of the function (defaults to this)
10667 filterBy : function(fn, scope){
10668 this.snapshot = this.snapshot || this.data;
10669 this.data = this.queryBy(fn, scope||this);
10670 this.fireEvent("datachanged", this);
10674 * Query the records by a specified property.
10675 * @param {String} field A field on your records
10676 * @param {String/RegExp} value Either a string that the field
10677 * should start with or a RegExp to test against the field
10678 * @param {Boolean} anyMatch True to match any part not just the beginning
10679 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10681 query : function(property, value, anyMatch){
10682 var fn = this.createFilterFn(property, value, anyMatch);
10683 return fn ? this.queryBy(fn) : this.data.clone();
10687 * Query by a function. The specified function will be called with each
10688 * record in this data source. If the function returns true the record is included
10690 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10691 * @param {Object} scope (optional) The scope of the function (defaults to this)
10692 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10694 queryBy : function(fn, scope){
10695 var data = this.snapshot || this.data;
10696 return data.filterBy(fn, scope||this);
10700 * Collects unique values for a particular dataIndex from this store.
10701 * @param {String} dataIndex The property to collect
10702 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10703 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10704 * @return {Array} An array of the unique values
10706 collect : function(dataIndex, allowNull, bypassFilter){
10707 var d = (bypassFilter === true && this.snapshot) ?
10708 this.snapshot.items : this.data.items;
10709 var v, sv, r = [], l = {};
10710 for(var i = 0, len = d.length; i < len; i++){
10711 v = d[i].data[dataIndex];
10713 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10722 * Revert to a view of the Record cache with no filtering applied.
10723 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10725 clearFilter : function(suppressEvent){
10726 if(this.snapshot && this.snapshot != this.data){
10727 this.data = this.snapshot;
10728 delete this.snapshot;
10729 if(suppressEvent !== true){
10730 this.fireEvent("datachanged", this);
10736 afterEdit : function(record){
10737 if(this.modified.indexOf(record) == -1){
10738 this.modified.push(record);
10740 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10744 afterReject : function(record){
10745 this.modified.remove(record);
10746 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10750 afterCommit : function(record){
10751 this.modified.remove(record);
10752 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10756 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10757 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10759 commitChanges : function(){
10760 var m = this.modified.slice(0);
10761 this.modified = [];
10762 for(var i = 0, len = m.length; i < len; i++){
10768 * Cancel outstanding changes on all changed records.
10770 rejectChanges : function(){
10771 var m = this.modified.slice(0);
10772 this.modified = [];
10773 for(var i = 0, len = m.length; i < len; i++){
10778 onMetaChange : function(meta, rtype, o){
10779 this.recordType = rtype;
10780 this.fields = rtype.prototype.fields;
10781 delete this.snapshot;
10782 this.sortInfo = meta.sortInfo || this.sortInfo;
10783 this.modified = [];
10784 this.fireEvent('metachange', this, this.reader.meta);
10787 moveIndex : function(data, type)
10789 var index = this.indexOf(data);
10791 var newIndex = index + type;
10795 this.insert(newIndex, data);
10800 * Ext JS Library 1.1.1
10801 * Copyright(c) 2006-2007, Ext JS, LLC.
10803 * Originally Released Under LGPL - original licence link has changed is not relivant.
10806 * <script type="text/javascript">
10810 * @class Roo.data.SimpleStore
10811 * @extends Roo.data.Store
10812 * Small helper class to make creating Stores from Array data easier.
10813 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10814 * @cfg {Array} fields An array of field definition objects, or field name strings.
10815 * @cfg {Array} data The multi-dimensional array of data
10817 * @param {Object} config
10819 Roo.data.SimpleStore = function(config){
10820 Roo.data.SimpleStore.superclass.constructor.call(this, {
10822 reader: new Roo.data.ArrayReader({
10825 Roo.data.Record.create(config.fields)
10827 proxy : new Roo.data.MemoryProxy(config.data)
10831 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10833 * Ext JS Library 1.1.1
10834 * Copyright(c) 2006-2007, Ext JS, LLC.
10836 * Originally Released Under LGPL - original licence link has changed is not relivant.
10839 * <script type="text/javascript">
10844 * @extends Roo.data.Store
10845 * @class Roo.data.JsonStore
10846 * Small helper class to make creating Stores for JSON data easier. <br/>
10848 var store = new Roo.data.JsonStore({
10849 url: 'get-images.php',
10851 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10854 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10855 * JsonReader and HttpProxy (unless inline data is provided).</b>
10856 * @cfg {Array} fields An array of field definition objects, or field name strings.
10858 * @param {Object} config
10860 Roo.data.JsonStore = function(c){
10861 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10862 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10863 reader: new Roo.data.JsonReader(c, c.fields)
10866 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10868 * Ext JS Library 1.1.1
10869 * Copyright(c) 2006-2007, Ext JS, LLC.
10871 * Originally Released Under LGPL - original licence link has changed is not relivant.
10874 * <script type="text/javascript">
10878 Roo.data.Field = function(config){
10879 if(typeof config == "string"){
10880 config = {name: config};
10882 Roo.apply(this, config);
10885 this.type = "auto";
10888 var st = Roo.data.SortTypes;
10889 // named sortTypes are supported, here we look them up
10890 if(typeof this.sortType == "string"){
10891 this.sortType = st[this.sortType];
10894 // set default sortType for strings and dates
10895 if(!this.sortType){
10898 this.sortType = st.asUCString;
10901 this.sortType = st.asDate;
10904 this.sortType = st.none;
10909 var stripRe = /[\$,%]/g;
10911 // prebuilt conversion function for this field, instead of
10912 // switching every time we're reading a value
10914 var cv, dateFormat = this.dateFormat;
10919 cv = function(v){ return v; };
10922 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10926 return v !== undefined && v !== null && v !== '' ?
10927 parseInt(String(v).replace(stripRe, ""), 10) : '';
10932 return v !== undefined && v !== null && v !== '' ?
10933 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10938 cv = function(v){ return v === true || v === "true" || v == 1; };
10945 if(v instanceof Date){
10949 if(dateFormat == "timestamp"){
10950 return new Date(v*1000);
10952 return Date.parseDate(v, dateFormat);
10954 var parsed = Date.parse(v);
10955 return parsed ? new Date(parsed) : null;
10964 Roo.data.Field.prototype = {
10972 * Ext JS Library 1.1.1
10973 * Copyright(c) 2006-2007, Ext JS, LLC.
10975 * Originally Released Under LGPL - original licence link has changed is not relivant.
10978 * <script type="text/javascript">
10981 // Base class for reading structured data from a data source. This class is intended to be
10982 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10985 * @class Roo.data.DataReader
10986 * Base class for reading structured data from a data source. This class is intended to be
10987 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10990 Roo.data.DataReader = function(meta, recordType){
10994 this.recordType = recordType instanceof Array ?
10995 Roo.data.Record.create(recordType) : recordType;
10998 Roo.data.DataReader.prototype = {
11000 * Create an empty record
11001 * @param {Object} data (optional) - overlay some values
11002 * @return {Roo.data.Record} record created.
11004 newRow : function(d) {
11006 this.recordType.prototype.fields.each(function(c) {
11008 case 'int' : da[c.name] = 0; break;
11009 case 'date' : da[c.name] = new Date(); break;
11010 case 'float' : da[c.name] = 0.0; break;
11011 case 'boolean' : da[c.name] = false; break;
11012 default : da[c.name] = ""; break;
11016 return new this.recordType(Roo.apply(da, d));
11021 * Ext JS Library 1.1.1
11022 * Copyright(c) 2006-2007, Ext JS, LLC.
11024 * Originally Released Under LGPL - original licence link has changed is not relivant.
11027 * <script type="text/javascript">
11031 * @class Roo.data.DataProxy
11032 * @extends Roo.data.Observable
11033 * This class is an abstract base class for implementations which provide retrieval of
11034 * unformatted data objects.<br>
11036 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11037 * (of the appropriate type which knows how to parse the data object) to provide a block of
11038 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11040 * Custom implementations must implement the load method as described in
11041 * {@link Roo.data.HttpProxy#load}.
11043 Roo.data.DataProxy = function(){
11046 * @event beforeload
11047 * Fires before a network request is made to retrieve a data object.
11048 * @param {Object} This DataProxy object.
11049 * @param {Object} params The params parameter to the load function.
11054 * Fires before the load method's callback is called.
11055 * @param {Object} This DataProxy object.
11056 * @param {Object} o The data object.
11057 * @param {Object} arg The callback argument object passed to the load function.
11061 * @event loadexception
11062 * Fires if an Exception occurs during data retrieval.
11063 * @param {Object} This DataProxy object.
11064 * @param {Object} o The data object.
11065 * @param {Object} arg The callback argument object passed to the load function.
11066 * @param {Object} e The Exception.
11068 loadexception : true
11070 Roo.data.DataProxy.superclass.constructor.call(this);
11073 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11076 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11080 * Ext JS Library 1.1.1
11081 * Copyright(c) 2006-2007, Ext JS, LLC.
11083 * Originally Released Under LGPL - original licence link has changed is not relivant.
11086 * <script type="text/javascript">
11089 * @class Roo.data.MemoryProxy
11090 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11091 * to the Reader when its load method is called.
11093 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11095 Roo.data.MemoryProxy = function(data){
11099 Roo.data.MemoryProxy.superclass.constructor.call(this);
11103 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11106 * Load data from the requested source (in this case an in-memory
11107 * data object passed to the constructor), read the data object into
11108 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11109 * process that block using the passed callback.
11110 * @param {Object} params This parameter is not used by the MemoryProxy class.
11111 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11112 * object into a block of Roo.data.Records.
11113 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11114 * The function must be passed <ul>
11115 * <li>The Record block object</li>
11116 * <li>The "arg" argument from the load function</li>
11117 * <li>A boolean success indicator</li>
11119 * @param {Object} scope The scope in which to call the callback
11120 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11122 load : function(params, reader, callback, scope, arg){
11123 params = params || {};
11126 result = reader.readRecords(this.data);
11128 this.fireEvent("loadexception", this, arg, null, e);
11129 callback.call(scope, null, arg, false);
11132 callback.call(scope, result, arg, true);
11136 update : function(params, records){
11141 * Ext JS Library 1.1.1
11142 * Copyright(c) 2006-2007, Ext JS, LLC.
11144 * Originally Released Under LGPL - original licence link has changed is not relivant.
11147 * <script type="text/javascript">
11150 * @class Roo.data.HttpProxy
11151 * @extends Roo.data.DataProxy
11152 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11153 * configured to reference a certain URL.<br><br>
11155 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11156 * from which the running page was served.<br><br>
11158 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11160 * Be aware that to enable the browser to parse an XML document, the server must set
11161 * the Content-Type header in the HTTP response to "text/xml".
11163 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11164 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11165 * will be used to make the request.
11167 Roo.data.HttpProxy = function(conn){
11168 Roo.data.HttpProxy.superclass.constructor.call(this);
11169 // is conn a conn config or a real conn?
11171 this.useAjax = !conn || !conn.events;
11175 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11176 // thse are take from connection...
11179 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11182 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11183 * extra parameters to each request made by this object. (defaults to undefined)
11186 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11187 * to each request made by this object. (defaults to undefined)
11190 * @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)
11193 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11196 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11202 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11206 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11207 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11208 * a finer-grained basis than the DataProxy events.
11210 getConnection : function(){
11211 return this.useAjax ? Roo.Ajax : this.conn;
11215 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11216 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11217 * process that block using the passed callback.
11218 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11219 * for the request to the remote server.
11220 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11221 * object into a block of Roo.data.Records.
11222 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11223 * The function must be passed <ul>
11224 * <li>The Record block object</li>
11225 * <li>The "arg" argument from the load function</li>
11226 * <li>A boolean success indicator</li>
11228 * @param {Object} scope The scope in which to call the callback
11229 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11231 load : function(params, reader, callback, scope, arg){
11232 if(this.fireEvent("beforeload", this, params) !== false){
11234 params : params || {},
11236 callback : callback,
11241 callback : this.loadResponse,
11245 Roo.applyIf(o, this.conn);
11246 if(this.activeRequest){
11247 Roo.Ajax.abort(this.activeRequest);
11249 this.activeRequest = Roo.Ajax.request(o);
11251 this.conn.request(o);
11254 callback.call(scope||this, null, arg, false);
11259 loadResponse : function(o, success, response){
11260 delete this.activeRequest;
11262 this.fireEvent("loadexception", this, o, response);
11263 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11268 result = o.reader.read(response);
11270 this.fireEvent("loadexception", this, o, response, e);
11271 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11275 this.fireEvent("load", this, o, o.request.arg);
11276 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11280 update : function(dataSet){
11285 updateResponse : function(dataSet){
11290 * Ext JS Library 1.1.1
11291 * Copyright(c) 2006-2007, Ext JS, LLC.
11293 * Originally Released Under LGPL - original licence link has changed is not relivant.
11296 * <script type="text/javascript">
11300 * @class Roo.data.ScriptTagProxy
11301 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11302 * other than the originating domain of the running page.<br><br>
11304 * <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
11305 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11307 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11308 * source code that is used as the source inside a <script> tag.<br><br>
11310 * In order for the browser to process the returned data, the server must wrap the data object
11311 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11312 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11313 * depending on whether the callback name was passed:
11316 boolean scriptTag = false;
11317 String cb = request.getParameter("callback");
11320 response.setContentType("text/javascript");
11322 response.setContentType("application/x-json");
11324 Writer out = response.getWriter();
11326 out.write(cb + "(");
11328 out.print(dataBlock.toJsonString());
11335 * @param {Object} config A configuration object.
11337 Roo.data.ScriptTagProxy = function(config){
11338 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11339 Roo.apply(this, config);
11340 this.head = document.getElementsByTagName("head")[0];
11343 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11345 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11347 * @cfg {String} url The URL from which to request the data object.
11350 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11354 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11355 * the server the name of the callback function set up by the load call to process the returned data object.
11356 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11357 * javascript output which calls this named function passing the data object as its only parameter.
11359 callbackParam : "callback",
11361 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11362 * name to the request.
11367 * Load data from the configured URL, read the data object into
11368 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11369 * process that block using the passed callback.
11370 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11371 * for the request to the remote server.
11372 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11373 * object into a block of Roo.data.Records.
11374 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11375 * The function must be passed <ul>
11376 * <li>The Record block object</li>
11377 * <li>The "arg" argument from the load function</li>
11378 * <li>A boolean success indicator</li>
11380 * @param {Object} scope The scope in which to call the callback
11381 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11383 load : function(params, reader, callback, scope, arg){
11384 if(this.fireEvent("beforeload", this, params) !== false){
11386 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11388 var url = this.url;
11389 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11391 url += "&_dc=" + (new Date().getTime());
11393 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11396 cb : "stcCallback"+transId,
11397 scriptId : "stcScript"+transId,
11401 callback : callback,
11407 window[trans.cb] = function(o){
11408 conn.handleResponse(o, trans);
11411 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11413 if(this.autoAbort !== false){
11417 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11419 var script = document.createElement("script");
11420 script.setAttribute("src", url);
11421 script.setAttribute("type", "text/javascript");
11422 script.setAttribute("id", trans.scriptId);
11423 this.head.appendChild(script);
11425 this.trans = trans;
11427 callback.call(scope||this, null, arg, false);
11432 isLoading : function(){
11433 return this.trans ? true : false;
11437 * Abort the current server request.
11439 abort : function(){
11440 if(this.isLoading()){
11441 this.destroyTrans(this.trans);
11446 destroyTrans : function(trans, isLoaded){
11447 this.head.removeChild(document.getElementById(trans.scriptId));
11448 clearTimeout(trans.timeoutId);
11450 window[trans.cb] = undefined;
11452 delete window[trans.cb];
11455 // if hasn't been loaded, wait for load to remove it to prevent script error
11456 window[trans.cb] = function(){
11457 window[trans.cb] = undefined;
11459 delete window[trans.cb];
11466 handleResponse : function(o, trans){
11467 this.trans = false;
11468 this.destroyTrans(trans, true);
11471 result = trans.reader.readRecords(o);
11473 this.fireEvent("loadexception", this, o, trans.arg, e);
11474 trans.callback.call(trans.scope||window, null, trans.arg, false);
11477 this.fireEvent("load", this, o, trans.arg);
11478 trans.callback.call(trans.scope||window, result, trans.arg, true);
11482 handleFailure : function(trans){
11483 this.trans = false;
11484 this.destroyTrans(trans, false);
11485 this.fireEvent("loadexception", this, null, trans.arg);
11486 trans.callback.call(trans.scope||window, null, trans.arg, false);
11490 * Ext JS Library 1.1.1
11491 * Copyright(c) 2006-2007, Ext JS, LLC.
11493 * Originally Released Under LGPL - original licence link has changed is not relivant.
11496 * <script type="text/javascript">
11500 * @class Roo.data.JsonReader
11501 * @extends Roo.data.DataReader
11502 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11503 * based on mappings in a provided Roo.data.Record constructor.
11505 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11506 * in the reply previously.
11511 var RecordDef = Roo.data.Record.create([
11512 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11513 {name: 'occupation'} // This field will use "occupation" as the mapping.
11515 var myReader = new Roo.data.JsonReader({
11516 totalProperty: "results", // The property which contains the total dataset size (optional)
11517 root: "rows", // The property which contains an Array of row objects
11518 id: "id" // The property within each row object that provides an ID for the record (optional)
11522 * This would consume a JSON file like this:
11524 { 'results': 2, 'rows': [
11525 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11526 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11529 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11530 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11531 * paged from the remote server.
11532 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11533 * @cfg {String} root name of the property which contains the Array of row objects.
11534 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11535 * @cfg {Array} fields Array of field definition objects
11537 * Create a new JsonReader
11538 * @param {Object} meta Metadata configuration options
11539 * @param {Object} recordType Either an Array of field definition objects,
11540 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11542 Roo.data.JsonReader = function(meta, recordType){
11545 // set some defaults:
11546 Roo.applyIf(meta, {
11547 totalProperty: 'total',
11548 successProperty : 'success',
11553 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11555 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11558 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11559 * Used by Store query builder to append _requestMeta to params.
11562 metaFromRemote : false,
11564 * This method is only used by a DataProxy which has retrieved data from a remote server.
11565 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11566 * @return {Object} data A data block which is used by an Roo.data.Store object as
11567 * a cache of Roo.data.Records.
11569 read : function(response){
11570 var json = response.responseText;
11572 var o = /* eval:var:o */ eval("("+json+")");
11574 throw {message: "JsonReader.read: Json object not found"};
11580 this.metaFromRemote = true;
11581 this.meta = o.metaData;
11582 this.recordType = Roo.data.Record.create(o.metaData.fields);
11583 this.onMetaChange(this.meta, this.recordType, o);
11585 return this.readRecords(o);
11588 // private function a store will implement
11589 onMetaChange : function(meta, recordType, o){
11596 simpleAccess: function(obj, subsc) {
11603 getJsonAccessor: function(){
11605 return function(expr) {
11607 return(re.test(expr))
11608 ? new Function("obj", "return obj." + expr)
11613 return Roo.emptyFn;
11618 * Create a data block containing Roo.data.Records from an XML document.
11619 * @param {Object} o An object which contains an Array of row objects in the property specified
11620 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11621 * which contains the total size of the dataset.
11622 * @return {Object} data A data block which is used by an Roo.data.Store object as
11623 * a cache of Roo.data.Records.
11625 readRecords : function(o){
11627 * After any data loads, the raw JSON data is available for further custom processing.
11631 var s = this.meta, Record = this.recordType,
11632 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11634 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11636 if(s.totalProperty) {
11637 this.getTotal = this.getJsonAccessor(s.totalProperty);
11639 if(s.successProperty) {
11640 this.getSuccess = this.getJsonAccessor(s.successProperty);
11642 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11644 var g = this.getJsonAccessor(s.id);
11645 this.getId = function(rec) {
11647 return (r === undefined || r === "") ? null : r;
11650 this.getId = function(){return null;};
11653 for(var jj = 0; jj < fl; jj++){
11655 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11656 this.ef[jj] = this.getJsonAccessor(map);
11660 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11661 if(s.totalProperty){
11662 var vt = parseInt(this.getTotal(o), 10);
11667 if(s.successProperty){
11668 var vs = this.getSuccess(o);
11669 if(vs === false || vs === 'false'){
11674 for(var i = 0; i < c; i++){
11677 var id = this.getId(n);
11678 for(var j = 0; j < fl; j++){
11680 var v = this.ef[j](n);
11682 Roo.log('missing convert for ' + f.name);
11686 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11688 var record = new Record(values, id);
11690 records[i] = record;
11696 totalRecords : totalRecords
11701 * Ext JS Library 1.1.1
11702 * Copyright(c) 2006-2007, Ext JS, LLC.
11704 * Originally Released Under LGPL - original licence link has changed is not relivant.
11707 * <script type="text/javascript">
11711 * @class Roo.data.ArrayReader
11712 * @extends Roo.data.DataReader
11713 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11714 * Each element of that Array represents a row of data fields. The
11715 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11716 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11720 var RecordDef = Roo.data.Record.create([
11721 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11722 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11724 var myReader = new Roo.data.ArrayReader({
11725 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11729 * This would consume an Array like this:
11731 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11733 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11735 * Create a new JsonReader
11736 * @param {Object} meta Metadata configuration options.
11737 * @param {Object} recordType Either an Array of field definition objects
11738 * as specified to {@link Roo.data.Record#create},
11739 * or an {@link Roo.data.Record} object
11740 * created using {@link Roo.data.Record#create}.
11742 Roo.data.ArrayReader = function(meta, recordType){
11743 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11746 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11748 * Create a data block containing Roo.data.Records from an XML document.
11749 * @param {Object} o An Array of row objects which represents the dataset.
11750 * @return {Object} data A data block which is used by an Roo.data.Store object as
11751 * a cache of Roo.data.Records.
11753 readRecords : function(o){
11754 var sid = this.meta ? this.meta.id : null;
11755 var recordType = this.recordType, fields = recordType.prototype.fields;
11758 for(var i = 0; i < root.length; i++){
11761 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11762 for(var j = 0, jlen = fields.length; j < jlen; j++){
11763 var f = fields.items[j];
11764 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11765 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11767 values[f.name] = v;
11769 var record = new recordType(values, id);
11771 records[records.length] = record;
11775 totalRecords : records.length
11784 * @class Roo.bootstrap.ComboBox
11785 * @extends Roo.bootstrap.TriggerField
11786 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11787 * @cfg {Boolean} append (true|false) default false
11788 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11789 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11790 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11791 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11792 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11793 * @cfg {Boolean} animate default true
11794 * @cfg {Boolean} emptyResultText only for touch device
11795 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11797 * Create a new ComboBox.
11798 * @param {Object} config Configuration options
11800 Roo.bootstrap.ComboBox = function(config){
11801 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11805 * Fires when the dropdown list is expanded
11806 * @param {Roo.bootstrap.ComboBox} combo This combo box
11811 * Fires when the dropdown list is collapsed
11812 * @param {Roo.bootstrap.ComboBox} combo This combo box
11816 * @event beforeselect
11817 * Fires before a list item is selected. Return false to cancel the selection.
11818 * @param {Roo.bootstrap.ComboBox} combo This combo box
11819 * @param {Roo.data.Record} record The data record returned from the underlying store
11820 * @param {Number} index The index of the selected item in the dropdown list
11822 'beforeselect' : true,
11825 * Fires when a list item is selected
11826 * @param {Roo.bootstrap.ComboBox} combo This combo box
11827 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11828 * @param {Number} index The index of the selected item in the dropdown list
11832 * @event beforequery
11833 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11834 * The event object passed has these properties:
11835 * @param {Roo.bootstrap.ComboBox} combo This combo box
11836 * @param {String} query The query
11837 * @param {Boolean} forceAll true to force "all" query
11838 * @param {Boolean} cancel true to cancel the query
11839 * @param {Object} e The query event object
11841 'beforequery': true,
11844 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11845 * @param {Roo.bootstrap.ComboBox} combo This combo box
11850 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11851 * @param {Roo.bootstrap.ComboBox} combo This combo box
11852 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11857 * Fires when the remove value from the combobox array
11858 * @param {Roo.bootstrap.ComboBox} combo This combo box
11862 * @event afterremove
11863 * Fires when the remove value from the combobox array
11864 * @param {Roo.bootstrap.ComboBox} combo This combo box
11866 'afterremove' : true,
11868 * @event specialfilter
11869 * Fires when specialfilter
11870 * @param {Roo.bootstrap.ComboBox} combo This combo box
11872 'specialfilter' : true,
11875 * Fires when tick the element
11876 * @param {Roo.bootstrap.ComboBox} combo This combo box
11880 * @event touchviewdisplay
11881 * Fires when touch view require special display (default is using displayField)
11882 * @param {Roo.bootstrap.ComboBox} combo This combo box
11883 * @param {Object} cfg set html .
11885 'touchviewdisplay' : true
11890 this.tickItems = [];
11892 this.selectedIndex = -1;
11893 if(this.mode == 'local'){
11894 if(config.queryDelay === undefined){
11895 this.queryDelay = 10;
11897 if(config.minChars === undefined){
11903 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11906 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11907 * rendering into an Roo.Editor, defaults to false)
11910 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11911 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11914 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11917 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11918 * the dropdown list (defaults to undefined, with no header element)
11922 * @cfg {String/Roo.Template} tpl The template to use to render the output
11926 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11928 listWidth: undefined,
11930 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11931 * mode = 'remote' or 'text' if mode = 'local')
11933 displayField: undefined,
11936 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11937 * mode = 'remote' or 'value' if mode = 'local').
11938 * Note: use of a valueField requires the user make a selection
11939 * in order for a value to be mapped.
11941 valueField: undefined,
11945 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11946 * field's data value (defaults to the underlying DOM element's name)
11948 hiddenName: undefined,
11950 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11954 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11956 selectedClass: 'active',
11959 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11963 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11964 * anchor positions (defaults to 'tl-bl')
11966 listAlign: 'tl-bl?',
11968 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11972 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11973 * query specified by the allQuery config option (defaults to 'query')
11975 triggerAction: 'query',
11977 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11978 * (defaults to 4, does not apply if editable = false)
11982 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11983 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11987 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11988 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11992 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11993 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11997 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11998 * when editable = true (defaults to false)
12000 selectOnFocus:false,
12002 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12004 queryParam: 'query',
12006 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12007 * when mode = 'remote' (defaults to 'Loading...')
12009 loadingText: 'Loading...',
12011 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12015 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12019 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12020 * traditional select (defaults to true)
12024 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12028 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12032 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12033 * listWidth has a higher value)
12037 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12038 * allow the user to set arbitrary text into the field (defaults to false)
12040 forceSelection:false,
12042 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12043 * if typeAhead = true (defaults to 250)
12045 typeAheadDelay : 250,
12047 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12048 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12050 valueNotFoundText : undefined,
12052 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12054 blockFocus : false,
12057 * @cfg {Boolean} disableClear Disable showing of clear button.
12059 disableClear : false,
12061 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12063 alwaysQuery : false,
12066 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12071 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12073 invalidClass : "has-warning",
12076 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12078 validClass : "has-success",
12081 * @cfg {Boolean} specialFilter (true|false) special filter default false
12083 specialFilter : false,
12086 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12088 mobileTouchView : true,
12100 btnPosition : 'right',
12101 triggerList : true,
12102 showToggleBtn : true,
12104 emptyResultText: 'Empty',
12105 triggerText : 'Select',
12107 // element that contains real text value.. (when hidden is used..)
12109 getAutoCreate : function()
12117 if(Roo.isTouch && this.mobileTouchView){
12118 cfg = this.getAutoCreateTouchView();
12125 if(!this.tickable){
12126 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12131 * ComboBox with tickable selections
12134 var align = this.labelAlign || this.parentLabelAlign();
12137 cls : 'form-group roo-combobox-tickable' //input-group
12142 cls : 'tickable-buttons',
12147 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12148 html : this.triggerText
12154 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12161 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12168 buttons.cn.unshift({
12170 cls: 'roo-select2-search-field-input'
12176 Roo.each(buttons.cn, function(c){
12178 c.cls += ' btn-' + _this.size;
12181 if (_this.disabled) {
12192 cls: 'form-hidden-field'
12196 cls: 'roo-select2-choices',
12200 cls: 'roo-select2-search-field',
12212 cls: 'roo-select2-container input-group roo-select2-container-multi',
12217 // cls: 'typeahead typeahead-long dropdown-menu',
12218 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12223 if(this.hasFeedback && !this.allowBlank){
12227 cls: 'glyphicon form-control-feedback'
12230 combobox.cn.push(feedback);
12233 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12235 // Roo.log("left and has label");
12241 cls : 'control-label col-sm-' + this.labelWidth,
12242 html : this.fieldLabel
12246 cls : "col-sm-" + (12 - this.labelWidth),
12253 } else if ( this.fieldLabel.length) {
12254 // Roo.log(" label");
12259 //cls : 'input-group-addon',
12260 html : this.fieldLabel
12270 // Roo.log(" no label && no align");
12277 ['xs','sm','md','lg'].map(function(size){
12278 if (settings[size]) {
12279 cfg.cls += ' col-' + size + '-' + settings[size];
12287 _initEventsCalled : false,
12290 initEvents: function()
12293 if (this._initEventsCalled) { // as we call render... prevent looping...
12296 this._initEventsCalled = true;
12299 throw "can not find store for combo";
12302 this.store = Roo.factory(this.store, Roo.data);
12304 // if we are building from html. then this element is so complex, that we can not really
12305 // use the rendered HTML.
12306 // so we have to trash and replace the previous code.
12307 if (Roo.XComponent.build_from_html) {
12309 // remove this element....
12310 var e = this.el.dom, k=0;
12311 while (e ) { e = e.previousSibling; ++k;}
12316 this.rendered = false;
12318 this.render(this.parent().getChildContainer(true), k);
12329 if(Roo.isTouch && this.mobileTouchView){
12330 this.initTouchView();
12335 this.initTickableEvents();
12339 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12341 if(this.hiddenName){
12343 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12345 this.hiddenField.dom.value =
12346 this.hiddenValue !== undefined ? this.hiddenValue :
12347 this.value !== undefined ? this.value : '';
12349 // prevent input submission
12350 this.el.dom.removeAttribute('name');
12351 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12356 // this.el.dom.setAttribute('autocomplete', 'off');
12359 var cls = 'x-combo-list';
12361 //this.list = new Roo.Layer({
12362 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12368 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12369 _this.list.setWidth(lw);
12372 this.list.on('mouseover', this.onViewOver, this);
12373 this.list.on('mousemove', this.onViewMove, this);
12375 this.list.on('scroll', this.onViewScroll, this);
12378 this.list.swallowEvent('mousewheel');
12379 this.assetHeight = 0;
12382 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12383 this.assetHeight += this.header.getHeight();
12386 this.innerList = this.list.createChild({cls:cls+'-inner'});
12387 this.innerList.on('mouseover', this.onViewOver, this);
12388 this.innerList.on('mousemove', this.onViewMove, this);
12389 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12391 if(this.allowBlank && !this.pageSize && !this.disableClear){
12392 this.footer = this.list.createChild({cls:cls+'-ft'});
12393 this.pageTb = new Roo.Toolbar(this.footer);
12397 this.footer = this.list.createChild({cls:cls+'-ft'});
12398 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12399 {pageSize: this.pageSize});
12403 if (this.pageTb && this.allowBlank && !this.disableClear) {
12405 this.pageTb.add(new Roo.Toolbar.Fill(), {
12406 cls: 'x-btn-icon x-btn-clear',
12408 handler: function()
12411 _this.clearValue();
12412 _this.onSelect(false, -1);
12417 this.assetHeight += this.footer.getHeight();
12422 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12425 this.view = new Roo.View(this.list, this.tpl, {
12426 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12428 //this.view.wrapEl.setDisplayed(false);
12429 this.view.on('click', this.onViewClick, this);
12433 this.store.on('beforeload', this.onBeforeLoad, this);
12434 this.store.on('load', this.onLoad, this);
12435 this.store.on('loadexception', this.onLoadException, this);
12437 if(this.resizable){
12438 this.resizer = new Roo.Resizable(this.list, {
12439 pinned:true, handles:'se'
12441 this.resizer.on('resize', function(r, w, h){
12442 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12443 this.listWidth = w;
12444 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12445 this.restrictHeight();
12447 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12450 if(!this.editable){
12451 this.editable = true;
12452 this.setEditable(false);
12457 if (typeof(this.events.add.listeners) != 'undefined') {
12459 this.addicon = this.wrap.createChild(
12460 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12462 this.addicon.on('click', function(e) {
12463 this.fireEvent('add', this);
12466 if (typeof(this.events.edit.listeners) != 'undefined') {
12468 this.editicon = this.wrap.createChild(
12469 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12470 if (this.addicon) {
12471 this.editicon.setStyle('margin-left', '40px');
12473 this.editicon.on('click', function(e) {
12475 // we fire even if inothing is selected..
12476 this.fireEvent('edit', this, this.lastData );
12482 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12483 "up" : function(e){
12484 this.inKeyMode = true;
12488 "down" : function(e){
12489 if(!this.isExpanded()){
12490 this.onTriggerClick();
12492 this.inKeyMode = true;
12497 "enter" : function(e){
12498 // this.onViewClick();
12502 if(this.fireEvent("specialkey", this, e)){
12503 this.onViewClick(false);
12509 "esc" : function(e){
12513 "tab" : function(e){
12516 if(this.fireEvent("specialkey", this, e)){
12517 this.onViewClick(false);
12525 doRelay : function(foo, bar, hname){
12526 if(hname == 'down' || this.scope.isExpanded()){
12527 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12536 this.queryDelay = Math.max(this.queryDelay || 10,
12537 this.mode == 'local' ? 10 : 250);
12540 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12542 if(this.typeAhead){
12543 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12545 if(this.editable !== false){
12546 this.inputEl().on("keyup", this.onKeyUp, this);
12548 if(this.forceSelection){
12549 this.inputEl().on('blur', this.doForce, this);
12553 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12554 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12558 initTickableEvents: function()
12562 if(this.hiddenName){
12564 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12566 this.hiddenField.dom.value =
12567 this.hiddenValue !== undefined ? this.hiddenValue :
12568 this.value !== undefined ? this.value : '';
12570 // prevent input submission
12571 this.el.dom.removeAttribute('name');
12572 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12577 // this.list = this.el.select('ul.dropdown-menu',true).first();
12579 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12580 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12581 if(this.triggerList){
12582 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12585 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12586 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12588 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12589 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12591 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12592 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12594 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12595 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12596 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12599 this.cancelBtn.hide();
12604 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12605 _this.list.setWidth(lw);
12608 this.list.on('mouseover', this.onViewOver, this);
12609 this.list.on('mousemove', this.onViewMove, this);
12611 this.list.on('scroll', this.onViewScroll, this);
12614 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>';
12617 this.view = new Roo.View(this.list, this.tpl, {
12618 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12621 //this.view.wrapEl.setDisplayed(false);
12622 this.view.on('click', this.onViewClick, this);
12626 this.store.on('beforeload', this.onBeforeLoad, this);
12627 this.store.on('load', this.onLoad, this);
12628 this.store.on('loadexception', this.onLoadException, this);
12631 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12632 "up" : function(e){
12633 this.inKeyMode = true;
12637 "down" : function(e){
12638 this.inKeyMode = true;
12642 "enter" : function(e){
12643 if(this.fireEvent("specialkey", this, e)){
12644 this.onViewClick(false);
12650 "esc" : function(e){
12651 this.onTickableFooterButtonClick(e, false, false);
12654 "tab" : function(e){
12655 this.fireEvent("specialkey", this, e);
12657 this.onTickableFooterButtonClick(e, false, false);
12664 doRelay : function(e, fn, key){
12665 if(this.scope.isExpanded()){
12666 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12675 this.queryDelay = Math.max(this.queryDelay || 10,
12676 this.mode == 'local' ? 10 : 250);
12679 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12681 if(this.typeAhead){
12682 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12685 if(this.editable !== false){
12686 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12691 onDestroy : function(){
12693 this.view.setStore(null);
12694 this.view.el.removeAllListeners();
12695 this.view.el.remove();
12696 this.view.purgeListeners();
12699 this.list.dom.innerHTML = '';
12703 this.store.un('beforeload', this.onBeforeLoad, this);
12704 this.store.un('load', this.onLoad, this);
12705 this.store.un('loadexception', this.onLoadException, this);
12707 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12711 fireKey : function(e){
12712 if(e.isNavKeyPress() && !this.list.isVisible()){
12713 this.fireEvent("specialkey", this, e);
12718 onResize: function(w, h){
12719 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12721 // if(typeof w != 'number'){
12722 // // we do not handle it!?!?
12725 // var tw = this.trigger.getWidth();
12726 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12727 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12729 // this.inputEl().setWidth( this.adjustWidth('input', x));
12731 // //this.trigger.setStyle('left', x+'px');
12733 // if(this.list && this.listWidth === undefined){
12734 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12735 // this.list.setWidth(lw);
12736 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12744 * Allow or prevent the user from directly editing the field text. If false is passed,
12745 * the user will only be able to select from the items defined in the dropdown list. This method
12746 * is the runtime equivalent of setting the 'editable' config option at config time.
12747 * @param {Boolean} value True to allow the user to directly edit the field text
12749 setEditable : function(value){
12750 if(value == this.editable){
12753 this.editable = value;
12755 this.inputEl().dom.setAttribute('readOnly', true);
12756 this.inputEl().on('mousedown', this.onTriggerClick, this);
12757 this.inputEl().addClass('x-combo-noedit');
12759 this.inputEl().dom.setAttribute('readOnly', false);
12760 this.inputEl().un('mousedown', this.onTriggerClick, this);
12761 this.inputEl().removeClass('x-combo-noedit');
12767 onBeforeLoad : function(combo,opts){
12768 if(!this.hasFocus){
12772 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12774 this.restrictHeight();
12775 this.selectedIndex = -1;
12779 onLoad : function(){
12781 this.hasQuery = false;
12783 if(!this.hasFocus){
12787 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12788 this.loading.hide();
12791 if(this.store.getCount() > 0){
12793 this.restrictHeight();
12794 if(this.lastQuery == this.allQuery){
12795 if(this.editable && !this.tickable){
12796 this.inputEl().dom.select();
12800 !this.selectByValue(this.value, true) &&
12803 !this.store.lastOptions ||
12804 typeof(this.store.lastOptions.add) == 'undefined' ||
12805 this.store.lastOptions.add != true
12808 this.select(0, true);
12811 if(this.autoFocus){
12814 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12815 this.taTask.delay(this.typeAheadDelay);
12819 this.onEmptyResults();
12825 onLoadException : function()
12827 this.hasQuery = false;
12829 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12830 this.loading.hide();
12833 if(this.tickable && this.editable){
12838 // only causes errors at present
12839 //Roo.log(this.store.reader.jsonData);
12840 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12842 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12848 onTypeAhead : function(){
12849 if(this.store.getCount() > 0){
12850 var r = this.store.getAt(0);
12851 var newValue = r.data[this.displayField];
12852 var len = newValue.length;
12853 var selStart = this.getRawValue().length;
12855 if(selStart != len){
12856 this.setRawValue(newValue);
12857 this.selectText(selStart, newValue.length);
12863 onSelect : function(record, index){
12865 if(this.fireEvent('beforeselect', this, record, index) !== false){
12867 this.setFromData(index > -1 ? record.data : false);
12870 this.fireEvent('select', this, record, index);
12875 * Returns the currently selected field value or empty string if no value is set.
12876 * @return {String} value The selected value
12878 getValue : function(){
12881 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12884 if(this.valueField){
12885 return typeof this.value != 'undefined' ? this.value : '';
12887 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12892 * Clears any text/value currently set in the field
12894 clearValue : function(){
12895 if(this.hiddenField){
12896 this.hiddenField.dom.value = '';
12899 this.setRawValue('');
12900 this.lastSelectionText = '';
12901 this.lastData = false;
12903 var close = this.closeTriggerEl();
12912 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12913 * will be displayed in the field. If the value does not match the data value of an existing item,
12914 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12915 * Otherwise the field will be blank (although the value will still be set).
12916 * @param {String} value The value to match
12918 setValue : function(v){
12925 if(this.valueField){
12926 var r = this.findRecord(this.valueField, v);
12928 text = r.data[this.displayField];
12929 }else if(this.valueNotFoundText !== undefined){
12930 text = this.valueNotFoundText;
12933 this.lastSelectionText = text;
12934 if(this.hiddenField){
12935 this.hiddenField.dom.value = v;
12937 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12940 var close = this.closeTriggerEl();
12943 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12947 * @property {Object} the last set data for the element
12952 * Sets the value of the field based on a object which is related to the record format for the store.
12953 * @param {Object} value the value to set as. or false on reset?
12955 setFromData : function(o){
12962 var dv = ''; // display value
12963 var vv = ''; // value value..
12965 if (this.displayField) {
12966 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12968 // this is an error condition!!!
12969 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12972 if(this.valueField){
12973 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12976 var close = this.closeTriggerEl();
12979 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12982 if(this.hiddenField){
12983 this.hiddenField.dom.value = vv;
12985 this.lastSelectionText = dv;
12986 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12990 // no hidden field.. - we store the value in 'value', but still display
12991 // display field!!!!
12992 this.lastSelectionText = dv;
12993 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13000 reset : function(){
13001 // overridden so that last data is reset..
13008 this.setValue(this.originalValue);
13009 this.clearInvalid();
13010 this.lastData = false;
13012 this.view.clearSelections();
13016 findRecord : function(prop, value){
13018 if(this.store.getCount() > 0){
13019 this.store.each(function(r){
13020 if(r.data[prop] == value){
13030 getName: function()
13032 // returns hidden if it's set..
13033 if (!this.rendered) {return ''};
13034 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13038 onViewMove : function(e, t){
13039 this.inKeyMode = false;
13043 onViewOver : function(e, t){
13044 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13047 var item = this.view.findItemFromChild(t);
13050 var index = this.view.indexOf(item);
13051 this.select(index, false);
13056 onViewClick : function(view, doFocus, el, e)
13058 var index = this.view.getSelectedIndexes()[0];
13060 var r = this.store.getAt(index);
13064 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13071 Roo.each(this.tickItems, function(v,k){
13073 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13075 _this.tickItems.splice(k, 1);
13077 if(typeof(e) == 'undefined' && view == false){
13078 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13090 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13091 this.tickItems.push(r.data);
13094 if(typeof(e) == 'undefined' && view == false){
13095 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13102 this.onSelect(r, index);
13104 if(doFocus !== false && !this.blockFocus){
13105 this.inputEl().focus();
13110 restrictHeight : function(){
13111 //this.innerList.dom.style.height = '';
13112 //var inner = this.innerList.dom;
13113 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13114 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13115 //this.list.beginUpdate();
13116 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13117 this.list.alignTo(this.inputEl(), this.listAlign);
13118 this.list.alignTo(this.inputEl(), this.listAlign);
13119 //this.list.endUpdate();
13123 onEmptyResults : function(){
13125 if(this.tickable && this.editable){
13126 this.restrictHeight();
13134 * Returns true if the dropdown list is expanded, else false.
13136 isExpanded : function(){
13137 return this.list.isVisible();
13141 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13142 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13143 * @param {String} value The data value of the item to select
13144 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13145 * selected item if it is not currently in view (defaults to true)
13146 * @return {Boolean} True if the value matched an item in the list, else false
13148 selectByValue : function(v, scrollIntoView){
13149 if(v !== undefined && v !== null){
13150 var r = this.findRecord(this.valueField || this.displayField, v);
13152 this.select(this.store.indexOf(r), scrollIntoView);
13160 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13161 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13162 * @param {Number} index The zero-based index of the list item to select
13163 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13164 * selected item if it is not currently in view (defaults to true)
13166 select : function(index, scrollIntoView){
13167 this.selectedIndex = index;
13168 this.view.select(index);
13169 if(scrollIntoView !== false){
13170 var el = this.view.getNode(index);
13172 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13175 this.list.scrollChildIntoView(el, false);
13181 selectNext : function(){
13182 var ct = this.store.getCount();
13184 if(this.selectedIndex == -1){
13186 }else if(this.selectedIndex < ct-1){
13187 this.select(this.selectedIndex+1);
13193 selectPrev : function(){
13194 var ct = this.store.getCount();
13196 if(this.selectedIndex == -1){
13198 }else if(this.selectedIndex != 0){
13199 this.select(this.selectedIndex-1);
13205 onKeyUp : function(e){
13206 if(this.editable !== false && !e.isSpecialKey()){
13207 this.lastKey = e.getKey();
13208 this.dqTask.delay(this.queryDelay);
13213 validateBlur : function(){
13214 return !this.list || !this.list.isVisible();
13218 initQuery : function(){
13220 var v = this.getRawValue();
13222 if(this.tickable && this.editable){
13223 v = this.tickableInputEl().getValue();
13230 doForce : function(){
13231 if(this.inputEl().dom.value.length > 0){
13232 this.inputEl().dom.value =
13233 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13239 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13240 * query allowing the query action to be canceled if needed.
13241 * @param {String} query The SQL query to execute
13242 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13243 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13244 * saved in the current store (defaults to false)
13246 doQuery : function(q, forceAll){
13248 if(q === undefined || q === null){
13253 forceAll: forceAll,
13257 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13262 forceAll = qe.forceAll;
13263 if(forceAll === true || (q.length >= this.minChars)){
13265 this.hasQuery = true;
13267 if(this.lastQuery != q || this.alwaysQuery){
13268 this.lastQuery = q;
13269 if(this.mode == 'local'){
13270 this.selectedIndex = -1;
13272 this.store.clearFilter();
13275 if(this.specialFilter){
13276 this.fireEvent('specialfilter', this);
13281 this.store.filter(this.displayField, q);
13284 this.store.fireEvent("datachanged", this.store);
13291 this.store.baseParams[this.queryParam] = q;
13293 var options = {params : this.getParams(q)};
13296 options.add = true;
13297 options.params.start = this.page * this.pageSize;
13300 this.store.load(options);
13303 * this code will make the page width larger, at the beginning, the list not align correctly,
13304 * we should expand the list on onLoad
13305 * so command out it
13310 this.selectedIndex = -1;
13315 this.loadNext = false;
13319 getParams : function(q){
13321 //p[this.queryParam] = q;
13325 p.limit = this.pageSize;
13331 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13333 collapse : function(){
13334 if(!this.isExpanded()){
13341 this.hasFocus = false;
13343 this.cancelBtn.hide();
13344 this.trigger.show();
13347 this.tickableInputEl().dom.value = '';
13348 this.tickableInputEl().blur();
13353 Roo.get(document).un('mousedown', this.collapseIf, this);
13354 Roo.get(document).un('mousewheel', this.collapseIf, this);
13355 if (!this.editable) {
13356 Roo.get(document).un('keydown', this.listKeyPress, this);
13358 this.fireEvent('collapse', this);
13362 collapseIf : function(e){
13363 var in_combo = e.within(this.el);
13364 var in_list = e.within(this.list);
13365 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13367 if (in_combo || in_list || is_list) {
13368 //e.stopPropagation();
13373 this.onTickableFooterButtonClick(e, false, false);
13381 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13383 expand : function(){
13385 if(this.isExpanded() || !this.hasFocus){
13389 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13390 this.list.setWidth(lw);
13397 this.restrictHeight();
13401 this.tickItems = Roo.apply([], this.item);
13404 this.cancelBtn.show();
13405 this.trigger.hide();
13408 this.tickableInputEl().focus();
13413 Roo.get(document).on('mousedown', this.collapseIf, this);
13414 Roo.get(document).on('mousewheel', this.collapseIf, this);
13415 if (!this.editable) {
13416 Roo.get(document).on('keydown', this.listKeyPress, this);
13419 this.fireEvent('expand', this);
13423 // Implements the default empty TriggerField.onTriggerClick function
13424 onTriggerClick : function(e)
13426 Roo.log('trigger click');
13428 if(this.disabled || !this.triggerList){
13433 this.loadNext = false;
13435 if(this.isExpanded()){
13437 if (!this.blockFocus) {
13438 this.inputEl().focus();
13442 this.hasFocus = true;
13443 if(this.triggerAction == 'all') {
13444 this.doQuery(this.allQuery, true);
13446 this.doQuery(this.getRawValue());
13448 if (!this.blockFocus) {
13449 this.inputEl().focus();
13454 onTickableTriggerClick : function(e)
13461 this.loadNext = false;
13462 this.hasFocus = true;
13464 if(this.triggerAction == 'all') {
13465 this.doQuery(this.allQuery, true);
13467 this.doQuery(this.getRawValue());
13471 onSearchFieldClick : function(e)
13473 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13474 this.onTickableFooterButtonClick(e, false, false);
13478 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13483 this.loadNext = false;
13484 this.hasFocus = true;
13486 if(this.triggerAction == 'all') {
13487 this.doQuery(this.allQuery, true);
13489 this.doQuery(this.getRawValue());
13493 listKeyPress : function(e)
13495 //Roo.log('listkeypress');
13496 // scroll to first matching element based on key pres..
13497 if (e.isSpecialKey()) {
13500 var k = String.fromCharCode(e.getKey()).toUpperCase();
13503 var csel = this.view.getSelectedNodes();
13504 var cselitem = false;
13506 var ix = this.view.indexOf(csel[0]);
13507 cselitem = this.store.getAt(ix);
13508 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13514 this.store.each(function(v) {
13516 // start at existing selection.
13517 if (cselitem.id == v.id) {
13523 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13524 match = this.store.indexOf(v);
13530 if (match === false) {
13531 return true; // no more action?
13534 this.view.select(match);
13535 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13536 sn.scrollIntoView(sn.dom.parentNode, false);
13539 onViewScroll : function(e, t){
13541 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){
13545 this.hasQuery = true;
13547 this.loading = this.list.select('.loading', true).first();
13549 if(this.loading === null){
13550 this.list.createChild({
13552 cls: 'loading roo-select2-more-results roo-select2-active',
13553 html: 'Loading more results...'
13556 this.loading = this.list.select('.loading', true).first();
13558 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13560 this.loading.hide();
13563 this.loading.show();
13568 this.loadNext = true;
13570 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13575 addItem : function(o)
13577 var dv = ''; // display value
13579 if (this.displayField) {
13580 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13582 // this is an error condition!!!
13583 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13590 var choice = this.choices.createChild({
13592 cls: 'roo-select2-search-choice',
13601 cls: 'roo-select2-search-choice-close',
13606 }, this.searchField);
13608 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13610 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13618 this.inputEl().dom.value = '';
13623 onRemoveItem : function(e, _self, o)
13625 e.preventDefault();
13627 this.lastItem = Roo.apply([], this.item);
13629 var index = this.item.indexOf(o.data) * 1;
13632 Roo.log('not this item?!');
13636 this.item.splice(index, 1);
13641 this.fireEvent('remove', this, e);
13647 syncValue : function()
13649 if(!this.item.length){
13656 Roo.each(this.item, function(i){
13657 if(_this.valueField){
13658 value.push(i[_this.valueField]);
13665 this.value = value.join(',');
13667 if(this.hiddenField){
13668 this.hiddenField.dom.value = this.value;
13671 this.store.fireEvent("datachanged", this.store);
13674 clearItem : function()
13676 if(!this.multiple){
13682 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13690 if(this.tickable && !Roo.isTouch){
13691 this.view.refresh();
13695 inputEl: function ()
13697 if(Roo.isTouch && this.mobileTouchView){
13698 return this.el.select('input.form-control',true).first();
13702 return this.searchField;
13705 return this.el.select('input.form-control',true).first();
13709 onTickableFooterButtonClick : function(e, btn, el)
13711 e.preventDefault();
13713 this.lastItem = Roo.apply([], this.item);
13715 if(btn && btn.name == 'cancel'){
13716 this.tickItems = Roo.apply([], this.item);
13725 Roo.each(this.tickItems, function(o){
13733 validate : function()
13735 var v = this.getRawValue();
13738 v = this.getValue();
13741 if(this.disabled || this.allowBlank || v.length){
13746 this.markInvalid();
13750 tickableInputEl : function()
13752 if(!this.tickable || !this.editable){
13753 return this.inputEl();
13756 return this.inputEl().select('.roo-select2-search-field-input', true).first();
13760 getAutoCreateTouchView : function()
13765 cls: 'form-group' //input-group
13771 type : this.inputType,
13772 cls : 'form-control x-combo-noedit',
13773 autocomplete: 'new-password',
13774 placeholder : this.placeholder || '',
13779 input.name = this.name;
13783 input.cls += ' input-' + this.size;
13786 if (this.disabled) {
13787 input.disabled = true;
13798 inputblock.cls += ' input-group';
13800 inputblock.cn.unshift({
13802 cls : 'input-group-addon',
13807 if(this.removable && !this.multiple){
13808 inputblock.cls += ' roo-removable';
13810 inputblock.cn.push({
13813 cls : 'roo-combo-removable-btn close'
13817 if(this.hasFeedback && !this.allowBlank){
13819 inputblock.cls += ' has-feedback';
13821 inputblock.cn.push({
13823 cls: 'glyphicon form-control-feedback'
13830 inputblock.cls += (this.before) ? '' : ' input-group';
13832 inputblock.cn.push({
13834 cls : 'input-group-addon',
13845 cls: 'form-hidden-field'
13859 cls: 'form-hidden-field'
13863 cls: 'roo-select2-choices',
13867 cls: 'roo-select2-search-field',
13880 cls: 'roo-select2-container input-group',
13886 // if(!this.multiple && this.showToggleBtn){
13893 // if (this.caret != false) {
13896 // cls: 'fa fa-' + this.caret
13901 // combobox.cn.push({
13903 // cls : 'input-group-addon btn dropdown-toggle',
13908 // cls: 'combobox-clear',
13912 // cls: 'icon-remove'
13922 combobox.cls += ' roo-select2-container-multi';
13925 var align = this.labelAlign || this.parentLabelAlign();
13929 if(this.fieldLabel.length && this.labelWidth){
13931 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13932 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13937 cls : 'control-label ' + lw,
13938 html : this.fieldLabel
13950 var settings = this;
13952 ['xs','sm','md','lg'].map(function(size){
13953 if (settings[size]) {
13954 cfg.cls += ' col-' + size + '-' + settings[size];
13961 initTouchView : function()
13963 this.renderTouchView();
13965 this.touchViewEl.on('scroll', function(){
13966 this.el.dom.scrollTop = 0;
13969 this.originalValue = this.getValue();
13971 this.inputEl().on("touch", this.showTouchView, this);
13973 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13974 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13976 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13978 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13979 this.store.on('load', this.onTouchViewLoad, this);
13980 this.store.on('loadexception', this.onTouchViewLoadException, this);
13982 if(this.hiddenName){
13984 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13986 this.hiddenField.dom.value =
13987 this.hiddenValue !== undefined ? this.hiddenValue :
13988 this.value !== undefined ? this.value : '';
13990 this.el.dom.removeAttribute('name');
13991 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13995 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13996 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13999 if(this.removable && !this.multiple){
14000 var close = this.closeTriggerEl();
14002 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14003 close.on('click', this.removeBtnClick, this, close);
14007 * fix the bug in Safari iOS8
14009 this.inputEl().on("focus", function(e){
14010 document.activeElement.blur();
14018 renderTouchView : function()
14020 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14021 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14023 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14024 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14026 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14027 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14028 this.touchViewBodyEl.setStyle('overflow', 'auto');
14030 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14031 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14033 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14034 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14038 showTouchView : function()
14044 this.touchViewHeaderEl.hide();
14046 if(this.fieldLabel.length){
14047 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
14048 this.touchViewHeaderEl.show();
14051 this.touchViewEl.show();
14053 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14054 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14055 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14057 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14059 if(this.fieldLabel.length){
14060 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14063 this.touchViewBodyEl.setHeight(bodyHeight);
14067 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14069 this.touchViewEl.addClass('in');
14072 this.doTouchViewQuery();
14076 hideTouchView : function()
14078 this.touchViewEl.removeClass('in');
14082 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14084 this.touchViewEl.setStyle('display', 'none');
14089 setTouchViewValue : function()
14096 Roo.each(this.tickItems, function(o){
14101 this.hideTouchView();
14104 doTouchViewQuery : function()
14113 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14117 if(!this.alwaysQuery || this.mode == 'local'){
14118 this.onTouchViewLoad();
14125 onTouchViewBeforeLoad : function(combo,opts)
14131 onTouchViewLoad : function()
14133 if(this.store.getCount() < 1){
14134 this.onTouchViewEmptyResults();
14138 this.clearTouchView();
14140 var rawValue = this.getRawValue();
14142 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14144 this.tickItems = [];
14146 this.store.data.each(function(d, rowIndex){
14147 var row = this.touchViewListGroup.createChild(template);
14149 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14150 row.addClass(d.data.cls);
14153 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14156 html : d.data[this.displayField]
14159 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14160 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14163 row.removeClass('selected');
14164 if(!this.multiple && this.valueField &&
14165 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14168 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14169 row.addClass('selected');
14172 if(this.multiple && this.valueField &&
14173 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14177 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14178 this.tickItems.push(d.data);
14181 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14185 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14187 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14189 if(this.fieldLabel.length){
14190 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14193 var listHeight = this.touchViewListGroup.getHeight();
14197 if(firstChecked && listHeight > bodyHeight){
14198 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14203 onTouchViewLoadException : function()
14205 this.hideTouchView();
14208 onTouchViewEmptyResults : function()
14210 this.clearTouchView();
14212 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14214 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14218 clearTouchView : function()
14220 this.touchViewListGroup.dom.innerHTML = '';
14223 onTouchViewClick : function(e, el, o)
14225 e.preventDefault();
14228 var rowIndex = o.rowIndex;
14230 var r = this.store.getAt(rowIndex);
14232 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14234 if(!this.multiple){
14235 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14236 c.dom.removeAttribute('checked');
14239 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14241 this.setFromData(r.data);
14243 var close = this.closeTriggerEl();
14249 this.hideTouchView();
14251 this.fireEvent('select', this, r, rowIndex);
14256 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14257 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14258 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14262 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14263 this.addItem(r.data);
14264 this.tickItems.push(r.data);
14270 * @cfg {Boolean} grow
14274 * @cfg {Number} growMin
14278 * @cfg {Number} growMax
14287 Roo.apply(Roo.bootstrap.ComboBox, {
14291 cls: 'modal-header',
14313 cls: 'list-group-item',
14317 cls: 'roo-combobox-list-group-item-value'
14321 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14335 listItemCheckbox : {
14337 cls: 'list-group-item',
14341 cls: 'roo-combobox-list-group-item-value'
14345 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14361 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14366 cls: 'modal-footer',
14374 cls: 'col-xs-6 text-left',
14377 cls: 'btn btn-danger roo-touch-view-cancel',
14383 cls: 'col-xs-6 text-right',
14386 cls: 'btn btn-success roo-touch-view-ok',
14397 Roo.apply(Roo.bootstrap.ComboBox, {
14399 touchViewTemplate : {
14401 cls: 'modal fade roo-combobox-touch-view',
14405 cls: 'modal-dialog',
14406 style : 'position:fixed', // we have to fix position....
14410 cls: 'modal-content',
14412 Roo.bootstrap.ComboBox.header,
14413 Roo.bootstrap.ComboBox.body,
14414 Roo.bootstrap.ComboBox.footer
14423 * Ext JS Library 1.1.1
14424 * Copyright(c) 2006-2007, Ext JS, LLC.
14426 * Originally Released Under LGPL - original licence link has changed is not relivant.
14429 * <script type="text/javascript">
14434 * @extends Roo.util.Observable
14435 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14436 * This class also supports single and multi selection modes. <br>
14437 * Create a data model bound view:
14439 var store = new Roo.data.Store(...);
14441 var view = new Roo.View({
14443 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14445 singleSelect: true,
14446 selectedClass: "ydataview-selected",
14450 // listen for node click?
14451 view.on("click", function(vw, index, node, e){
14452 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14456 dataModel.load("foobar.xml");
14458 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14460 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14461 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14463 * Note: old style constructor is still suported (container, template, config)
14466 * Create a new View
14467 * @param {Object} config The config object
14470 Roo.View = function(config, depreciated_tpl, depreciated_config){
14472 this.parent = false;
14474 if (typeof(depreciated_tpl) == 'undefined') {
14475 // new way.. - universal constructor.
14476 Roo.apply(this, config);
14477 this.el = Roo.get(this.el);
14480 this.el = Roo.get(config);
14481 this.tpl = depreciated_tpl;
14482 Roo.apply(this, depreciated_config);
14484 this.wrapEl = this.el.wrap().wrap();
14485 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14488 if(typeof(this.tpl) == "string"){
14489 this.tpl = new Roo.Template(this.tpl);
14491 // support xtype ctors..
14492 this.tpl = new Roo.factory(this.tpl, Roo);
14496 this.tpl.compile();
14501 * @event beforeclick
14502 * Fires before a click is processed. Returns false to cancel the default action.
14503 * @param {Roo.View} this
14504 * @param {Number} index The index of the target node
14505 * @param {HTMLElement} node The target node
14506 * @param {Roo.EventObject} e The raw event object
14508 "beforeclick" : true,
14511 * Fires when a template node is clicked.
14512 * @param {Roo.View} this
14513 * @param {Number} index The index of the target node
14514 * @param {HTMLElement} node The target node
14515 * @param {Roo.EventObject} e The raw event object
14520 * Fires when a template node is double clicked.
14521 * @param {Roo.View} this
14522 * @param {Number} index The index of the target node
14523 * @param {HTMLElement} node The target node
14524 * @param {Roo.EventObject} e The raw event object
14528 * @event contextmenu
14529 * Fires when a template node is right clicked.
14530 * @param {Roo.View} this
14531 * @param {Number} index The index of the target node
14532 * @param {HTMLElement} node The target node
14533 * @param {Roo.EventObject} e The raw event object
14535 "contextmenu" : true,
14537 * @event selectionchange
14538 * Fires when the selected nodes change.
14539 * @param {Roo.View} this
14540 * @param {Array} selections Array of the selected nodes
14542 "selectionchange" : true,
14545 * @event beforeselect
14546 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14547 * @param {Roo.View} this
14548 * @param {HTMLElement} node The node to be selected
14549 * @param {Array} selections Array of currently selected nodes
14551 "beforeselect" : true,
14553 * @event preparedata
14554 * Fires on every row to render, to allow you to change the data.
14555 * @param {Roo.View} this
14556 * @param {Object} data to be rendered (change this)
14558 "preparedata" : true
14566 "click": this.onClick,
14567 "dblclick": this.onDblClick,
14568 "contextmenu": this.onContextMenu,
14572 this.selections = [];
14574 this.cmp = new Roo.CompositeElementLite([]);
14576 this.store = Roo.factory(this.store, Roo.data);
14577 this.setStore(this.store, true);
14580 if ( this.footer && this.footer.xtype) {
14582 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14584 this.footer.dataSource = this.store;
14585 this.footer.container = fctr;
14586 this.footer = Roo.factory(this.footer, Roo);
14587 fctr.insertFirst(this.el);
14589 // this is a bit insane - as the paging toolbar seems to detach the el..
14590 // dom.parentNode.parentNode.parentNode
14591 // they get detached?
14595 Roo.View.superclass.constructor.call(this);
14600 Roo.extend(Roo.View, Roo.util.Observable, {
14603 * @cfg {Roo.data.Store} store Data store to load data from.
14608 * @cfg {String|Roo.Element} el The container element.
14613 * @cfg {String|Roo.Template} tpl The template used by this View
14617 * @cfg {String} dataName the named area of the template to use as the data area
14618 * Works with domtemplates roo-name="name"
14622 * @cfg {String} selectedClass The css class to add to selected nodes
14624 selectedClass : "x-view-selected",
14626 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14631 * @cfg {String} text to display on mask (default Loading)
14635 * @cfg {Boolean} multiSelect Allow multiple selection
14637 multiSelect : false,
14639 * @cfg {Boolean} singleSelect Allow single selection
14641 singleSelect: false,
14644 * @cfg {Boolean} toggleSelect - selecting
14646 toggleSelect : false,
14649 * @cfg {Boolean} tickable - selecting
14654 * Returns the element this view is bound to.
14655 * @return {Roo.Element}
14657 getEl : function(){
14658 return this.wrapEl;
14664 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14666 refresh : function(){
14667 //Roo.log('refresh');
14670 // if we are using something like 'domtemplate', then
14671 // the what gets used is:
14672 // t.applySubtemplate(NAME, data, wrapping data..)
14673 // the outer template then get' applied with
14674 // the store 'extra data'
14675 // and the body get's added to the
14676 // roo-name="data" node?
14677 // <span class='roo-tpl-{name}'></span> ?????
14681 this.clearSelections();
14682 this.el.update("");
14684 var records = this.store.getRange();
14685 if(records.length < 1) {
14687 // is this valid?? = should it render a template??
14689 this.el.update(this.emptyText);
14693 if (this.dataName) {
14694 this.el.update(t.apply(this.store.meta)); //????
14695 el = this.el.child('.roo-tpl-' + this.dataName);
14698 for(var i = 0, len = records.length; i < len; i++){
14699 var data = this.prepareData(records[i].data, i, records[i]);
14700 this.fireEvent("preparedata", this, data, i, records[i]);
14702 var d = Roo.apply({}, data);
14705 Roo.apply(d, {'roo-id' : Roo.id()});
14709 Roo.each(this.parent.item, function(item){
14710 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14713 Roo.apply(d, {'roo-data-checked' : 'checked'});
14717 html[html.length] = Roo.util.Format.trim(
14719 t.applySubtemplate(this.dataName, d, this.store.meta) :
14726 el.update(html.join(""));
14727 this.nodes = el.dom.childNodes;
14728 this.updateIndexes(0);
14733 * Function to override to reformat the data that is sent to
14734 * the template for each node.
14735 * DEPRICATED - use the preparedata event handler.
14736 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14737 * a JSON object for an UpdateManager bound view).
14739 prepareData : function(data, index, record)
14741 this.fireEvent("preparedata", this, data, index, record);
14745 onUpdate : function(ds, record){
14746 // Roo.log('on update');
14747 this.clearSelections();
14748 var index = this.store.indexOf(record);
14749 var n = this.nodes[index];
14750 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14751 n.parentNode.removeChild(n);
14752 this.updateIndexes(index, index);
14758 onAdd : function(ds, records, index)
14760 //Roo.log(['on Add', ds, records, index] );
14761 this.clearSelections();
14762 if(this.nodes.length == 0){
14766 var n = this.nodes[index];
14767 for(var i = 0, len = records.length; i < len; i++){
14768 var d = this.prepareData(records[i].data, i, records[i]);
14770 this.tpl.insertBefore(n, d);
14773 this.tpl.append(this.el, d);
14776 this.updateIndexes(index);
14779 onRemove : function(ds, record, index){
14780 // Roo.log('onRemove');
14781 this.clearSelections();
14782 var el = this.dataName ?
14783 this.el.child('.roo-tpl-' + this.dataName) :
14786 el.dom.removeChild(this.nodes[index]);
14787 this.updateIndexes(index);
14791 * Refresh an individual node.
14792 * @param {Number} index
14794 refreshNode : function(index){
14795 this.onUpdate(this.store, this.store.getAt(index));
14798 updateIndexes : function(startIndex, endIndex){
14799 var ns = this.nodes;
14800 startIndex = startIndex || 0;
14801 endIndex = endIndex || ns.length - 1;
14802 for(var i = startIndex; i <= endIndex; i++){
14803 ns[i].nodeIndex = i;
14808 * Changes the data store this view uses and refresh the view.
14809 * @param {Store} store
14811 setStore : function(store, initial){
14812 if(!initial && this.store){
14813 this.store.un("datachanged", this.refresh);
14814 this.store.un("add", this.onAdd);
14815 this.store.un("remove", this.onRemove);
14816 this.store.un("update", this.onUpdate);
14817 this.store.un("clear", this.refresh);
14818 this.store.un("beforeload", this.onBeforeLoad);
14819 this.store.un("load", this.onLoad);
14820 this.store.un("loadexception", this.onLoad);
14824 store.on("datachanged", this.refresh, this);
14825 store.on("add", this.onAdd, this);
14826 store.on("remove", this.onRemove, this);
14827 store.on("update", this.onUpdate, this);
14828 store.on("clear", this.refresh, this);
14829 store.on("beforeload", this.onBeforeLoad, this);
14830 store.on("load", this.onLoad, this);
14831 store.on("loadexception", this.onLoad, this);
14839 * onbeforeLoad - masks the loading area.
14842 onBeforeLoad : function(store,opts)
14844 //Roo.log('onBeforeLoad');
14846 this.el.update("");
14848 this.el.mask(this.mask ? this.mask : "Loading" );
14850 onLoad : function ()
14857 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14858 * @param {HTMLElement} node
14859 * @return {HTMLElement} The template node
14861 findItemFromChild : function(node){
14862 var el = this.dataName ?
14863 this.el.child('.roo-tpl-' + this.dataName,true) :
14866 if(!node || node.parentNode == el){
14869 var p = node.parentNode;
14870 while(p && p != el){
14871 if(p.parentNode == el){
14880 onClick : function(e){
14881 var item = this.findItemFromChild(e.getTarget());
14883 var index = this.indexOf(item);
14884 if(this.onItemClick(item, index, e) !== false){
14885 this.fireEvent("click", this, index, item, e);
14888 this.clearSelections();
14893 onContextMenu : function(e){
14894 var item = this.findItemFromChild(e.getTarget());
14896 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14901 onDblClick : function(e){
14902 var item = this.findItemFromChild(e.getTarget());
14904 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14908 onItemClick : function(item, index, e)
14910 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14913 if (this.toggleSelect) {
14914 var m = this.isSelected(item) ? 'unselect' : 'select';
14917 _t[m](item, true, false);
14920 if(this.multiSelect || this.singleSelect){
14921 if(this.multiSelect && e.shiftKey && this.lastSelection){
14922 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14924 this.select(item, this.multiSelect && e.ctrlKey);
14925 this.lastSelection = item;
14928 if(!this.tickable){
14929 e.preventDefault();
14937 * Get the number of selected nodes.
14940 getSelectionCount : function(){
14941 return this.selections.length;
14945 * Get the currently selected nodes.
14946 * @return {Array} An array of HTMLElements
14948 getSelectedNodes : function(){
14949 return this.selections;
14953 * Get the indexes of the selected nodes.
14956 getSelectedIndexes : function(){
14957 var indexes = [], s = this.selections;
14958 for(var i = 0, len = s.length; i < len; i++){
14959 indexes.push(s[i].nodeIndex);
14965 * Clear all selections
14966 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14968 clearSelections : function(suppressEvent){
14969 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14970 this.cmp.elements = this.selections;
14971 this.cmp.removeClass(this.selectedClass);
14972 this.selections = [];
14973 if(!suppressEvent){
14974 this.fireEvent("selectionchange", this, this.selections);
14980 * Returns true if the passed node is selected
14981 * @param {HTMLElement/Number} node The node or node index
14982 * @return {Boolean}
14984 isSelected : function(node){
14985 var s = this.selections;
14989 node = this.getNode(node);
14990 return s.indexOf(node) !== -1;
14995 * @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
14996 * @param {Boolean} keepExisting (optional) true to keep existing selections
14997 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14999 select : function(nodeInfo, keepExisting, suppressEvent){
15000 if(nodeInfo instanceof Array){
15002 this.clearSelections(true);
15004 for(var i = 0, len = nodeInfo.length; i < len; i++){
15005 this.select(nodeInfo[i], true, true);
15009 var node = this.getNode(nodeInfo);
15010 if(!node || this.isSelected(node)){
15011 return; // already selected.
15014 this.clearSelections(true);
15017 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15018 Roo.fly(node).addClass(this.selectedClass);
15019 this.selections.push(node);
15020 if(!suppressEvent){
15021 this.fireEvent("selectionchange", this, this.selections);
15029 * @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
15030 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15031 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15033 unselect : function(nodeInfo, keepExisting, suppressEvent)
15035 if(nodeInfo instanceof Array){
15036 Roo.each(this.selections, function(s) {
15037 this.unselect(s, nodeInfo);
15041 var node = this.getNode(nodeInfo);
15042 if(!node || !this.isSelected(node)){
15043 //Roo.log("not selected");
15044 return; // not selected.
15048 Roo.each(this.selections, function(s) {
15050 Roo.fly(node).removeClass(this.selectedClass);
15057 this.selections= ns;
15058 this.fireEvent("selectionchange", this, this.selections);
15062 * Gets a template node.
15063 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15064 * @return {HTMLElement} The node or null if it wasn't found
15066 getNode : function(nodeInfo){
15067 if(typeof nodeInfo == "string"){
15068 return document.getElementById(nodeInfo);
15069 }else if(typeof nodeInfo == "number"){
15070 return this.nodes[nodeInfo];
15076 * Gets a range template nodes.
15077 * @param {Number} startIndex
15078 * @param {Number} endIndex
15079 * @return {Array} An array of nodes
15081 getNodes : function(start, end){
15082 var ns = this.nodes;
15083 start = start || 0;
15084 end = typeof end == "undefined" ? ns.length - 1 : end;
15087 for(var i = start; i <= end; i++){
15091 for(var i = start; i >= end; i--){
15099 * Finds the index of the passed node
15100 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15101 * @return {Number} The index of the node or -1
15103 indexOf : function(node){
15104 node = this.getNode(node);
15105 if(typeof node.nodeIndex == "number"){
15106 return node.nodeIndex;
15108 var ns = this.nodes;
15109 for(var i = 0, len = ns.length; i < len; i++){
15120 * based on jquery fullcalendar
15124 Roo.bootstrap = Roo.bootstrap || {};
15126 * @class Roo.bootstrap.Calendar
15127 * @extends Roo.bootstrap.Component
15128 * Bootstrap Calendar class
15129 * @cfg {Boolean} loadMask (true|false) default false
15130 * @cfg {Object} header generate the user specific header of the calendar, default false
15133 * Create a new Container
15134 * @param {Object} config The config object
15139 Roo.bootstrap.Calendar = function(config){
15140 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15144 * Fires when a date is selected
15145 * @param {DatePicker} this
15146 * @param {Date} date The selected date
15150 * @event monthchange
15151 * Fires when the displayed month changes
15152 * @param {DatePicker} this
15153 * @param {Date} date The selected month
15155 'monthchange': true,
15157 * @event evententer
15158 * Fires when mouse over an event
15159 * @param {Calendar} this
15160 * @param {event} Event
15162 'evententer': true,
15164 * @event eventleave
15165 * Fires when the mouse leaves an
15166 * @param {Calendar} this
15169 'eventleave': true,
15171 * @event eventclick
15172 * Fires when the mouse click an
15173 * @param {Calendar} this
15182 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15185 * @cfg {Number} startDay
15186 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15194 getAutoCreate : function(){
15197 var fc_button = function(name, corner, style, content ) {
15198 return Roo.apply({},{
15200 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15202 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15205 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15216 style : 'width:100%',
15223 cls : 'fc-header-left',
15225 fc_button('prev', 'left', 'arrow', '‹' ),
15226 fc_button('next', 'right', 'arrow', '›' ),
15227 { tag: 'span', cls: 'fc-header-space' },
15228 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15236 cls : 'fc-header-center',
15240 cls: 'fc-header-title',
15243 html : 'month / year'
15251 cls : 'fc-header-right',
15253 /* fc_button('month', 'left', '', 'month' ),
15254 fc_button('week', '', '', 'week' ),
15255 fc_button('day', 'right', '', 'day' )
15267 header = this.header;
15270 var cal_heads = function() {
15272 // fixme - handle this.
15274 for (var i =0; i < Date.dayNames.length; i++) {
15275 var d = Date.dayNames[i];
15278 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15279 html : d.substring(0,3)
15283 ret[0].cls += ' fc-first';
15284 ret[6].cls += ' fc-last';
15287 var cal_cell = function(n) {
15290 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15295 cls: 'fc-day-number',
15299 cls: 'fc-day-content',
15303 style: 'position: relative;' // height: 17px;
15315 var cal_rows = function() {
15318 for (var r = 0; r < 6; r++) {
15325 for (var i =0; i < Date.dayNames.length; i++) {
15326 var d = Date.dayNames[i];
15327 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15330 row.cn[0].cls+=' fc-first';
15331 row.cn[0].cn[0].style = 'min-height:90px';
15332 row.cn[6].cls+=' fc-last';
15336 ret[0].cls += ' fc-first';
15337 ret[4].cls += ' fc-prev-last';
15338 ret[5].cls += ' fc-last';
15345 cls: 'fc-border-separate',
15346 style : 'width:100%',
15354 cls : 'fc-first fc-last',
15372 cls : 'fc-content',
15373 style : "position: relative;",
15376 cls : 'fc-view fc-view-month fc-grid',
15377 style : 'position: relative',
15378 unselectable : 'on',
15381 cls : 'fc-event-container',
15382 style : 'position:absolute;z-index:8;top:0;left:0;'
15400 initEvents : function()
15403 throw "can not find store for calendar";
15409 style: "text-align:center",
15413 style: "background-color:white;width:50%;margin:250 auto",
15417 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15428 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15430 var size = this.el.select('.fc-content', true).first().getSize();
15431 this.maskEl.setSize(size.width, size.height);
15432 this.maskEl.enableDisplayMode("block");
15433 if(!this.loadMask){
15434 this.maskEl.hide();
15437 this.store = Roo.factory(this.store, Roo.data);
15438 this.store.on('load', this.onLoad, this);
15439 this.store.on('beforeload', this.onBeforeLoad, this);
15443 this.cells = this.el.select('.fc-day',true);
15444 //Roo.log(this.cells);
15445 this.textNodes = this.el.query('.fc-day-number');
15446 this.cells.addClassOnOver('fc-state-hover');
15448 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15449 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15450 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15451 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15453 this.on('monthchange', this.onMonthChange, this);
15455 this.update(new Date().clearTime());
15458 resize : function() {
15459 var sz = this.el.getSize();
15461 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15462 this.el.select('.fc-day-content div',true).setHeight(34);
15467 showPrevMonth : function(e){
15468 this.update(this.activeDate.add("mo", -1));
15470 showToday : function(e){
15471 this.update(new Date().clearTime());
15474 showNextMonth : function(e){
15475 this.update(this.activeDate.add("mo", 1));
15479 showPrevYear : function(){
15480 this.update(this.activeDate.add("y", -1));
15484 showNextYear : function(){
15485 this.update(this.activeDate.add("y", 1));
15490 update : function(date)
15492 var vd = this.activeDate;
15493 this.activeDate = date;
15494 // if(vd && this.el){
15495 // var t = date.getTime();
15496 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15497 // Roo.log('using add remove');
15499 // this.fireEvent('monthchange', this, date);
15501 // this.cells.removeClass("fc-state-highlight");
15502 // this.cells.each(function(c){
15503 // if(c.dateValue == t){
15504 // c.addClass("fc-state-highlight");
15505 // setTimeout(function(){
15506 // try{c.dom.firstChild.focus();}catch(e){}
15516 var days = date.getDaysInMonth();
15518 var firstOfMonth = date.getFirstDateOfMonth();
15519 var startingPos = firstOfMonth.getDay()-this.startDay;
15521 if(startingPos < this.startDay){
15525 var pm = date.add(Date.MONTH, -1);
15526 var prevStart = pm.getDaysInMonth()-startingPos;
15528 this.cells = this.el.select('.fc-day',true);
15529 this.textNodes = this.el.query('.fc-day-number');
15530 this.cells.addClassOnOver('fc-state-hover');
15532 var cells = this.cells.elements;
15533 var textEls = this.textNodes;
15535 Roo.each(cells, function(cell){
15536 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15539 days += startingPos;
15541 // convert everything to numbers so it's fast
15542 var day = 86400000;
15543 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15546 //Roo.log(prevStart);
15548 var today = new Date().clearTime().getTime();
15549 var sel = date.clearTime().getTime();
15550 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15551 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15552 var ddMatch = this.disabledDatesRE;
15553 var ddText = this.disabledDatesText;
15554 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15555 var ddaysText = this.disabledDaysText;
15556 var format = this.format;
15558 var setCellClass = function(cal, cell){
15562 //Roo.log('set Cell Class');
15564 var t = d.getTime();
15568 cell.dateValue = t;
15570 cell.className += " fc-today";
15571 cell.className += " fc-state-highlight";
15572 cell.title = cal.todayText;
15575 // disable highlight in other month..
15576 //cell.className += " fc-state-highlight";
15581 cell.className = " fc-state-disabled";
15582 cell.title = cal.minText;
15586 cell.className = " fc-state-disabled";
15587 cell.title = cal.maxText;
15591 if(ddays.indexOf(d.getDay()) != -1){
15592 cell.title = ddaysText;
15593 cell.className = " fc-state-disabled";
15596 if(ddMatch && format){
15597 var fvalue = d.dateFormat(format);
15598 if(ddMatch.test(fvalue)){
15599 cell.title = ddText.replace("%0", fvalue);
15600 cell.className = " fc-state-disabled";
15604 if (!cell.initialClassName) {
15605 cell.initialClassName = cell.dom.className;
15608 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15613 for(; i < startingPos; i++) {
15614 textEls[i].innerHTML = (++prevStart);
15615 d.setDate(d.getDate()+1);
15617 cells[i].className = "fc-past fc-other-month";
15618 setCellClass(this, cells[i]);
15623 for(; i < days; i++){
15624 intDay = i - startingPos + 1;
15625 textEls[i].innerHTML = (intDay);
15626 d.setDate(d.getDate()+1);
15628 cells[i].className = ''; // "x-date-active";
15629 setCellClass(this, cells[i]);
15633 for(; i < 42; i++) {
15634 textEls[i].innerHTML = (++extraDays);
15635 d.setDate(d.getDate()+1);
15637 cells[i].className = "fc-future fc-other-month";
15638 setCellClass(this, cells[i]);
15641 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15643 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15645 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15646 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15648 if(totalRows != 6){
15649 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15650 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15653 this.fireEvent('monthchange', this, date);
15657 if(!this.internalRender){
15658 var main = this.el.dom.firstChild;
15659 var w = main.offsetWidth;
15660 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15661 Roo.fly(main).setWidth(w);
15662 this.internalRender = true;
15663 // opera does not respect the auto grow header center column
15664 // then, after it gets a width opera refuses to recalculate
15665 // without a second pass
15666 if(Roo.isOpera && !this.secondPass){
15667 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15668 this.secondPass = true;
15669 this.update.defer(10, this, [date]);
15676 findCell : function(dt) {
15677 dt = dt.clearTime().getTime();
15679 this.cells.each(function(c){
15680 //Roo.log("check " +c.dateValue + '?=' + dt);
15681 if(c.dateValue == dt){
15691 findCells : function(ev) {
15692 var s = ev.start.clone().clearTime().getTime();
15694 var e= ev.end.clone().clearTime().getTime();
15697 this.cells.each(function(c){
15698 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15700 if(c.dateValue > e){
15703 if(c.dateValue < s){
15712 // findBestRow: function(cells)
15716 // for (var i =0 ; i < cells.length;i++) {
15717 // ret = Math.max(cells[i].rows || 0,ret);
15724 addItem : function(ev)
15726 // look for vertical location slot in
15727 var cells = this.findCells(ev);
15729 // ev.row = this.findBestRow(cells);
15731 // work out the location.
15735 for(var i =0; i < cells.length; i++) {
15737 cells[i].row = cells[0].row;
15740 cells[i].row = cells[i].row + 1;
15750 if (crow.start.getY() == cells[i].getY()) {
15752 crow.end = cells[i];
15769 cells[0].events.push(ev);
15771 this.calevents.push(ev);
15774 clearEvents: function() {
15776 if(!this.calevents){
15780 Roo.each(this.cells.elements, function(c){
15786 Roo.each(this.calevents, function(e) {
15787 Roo.each(e.els, function(el) {
15788 el.un('mouseenter' ,this.onEventEnter, this);
15789 el.un('mouseleave' ,this.onEventLeave, this);
15794 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15800 renderEvents: function()
15804 this.cells.each(function(c) {
15813 if(c.row != c.events.length){
15814 r = 4 - (4 - (c.row - c.events.length));
15817 c.events = ev.slice(0, r);
15818 c.more = ev.slice(r);
15820 if(c.more.length && c.more.length == 1){
15821 c.events.push(c.more.pop());
15824 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15828 this.cells.each(function(c) {
15830 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15833 for (var e = 0; e < c.events.length; e++){
15834 var ev = c.events[e];
15835 var rows = ev.rows;
15837 for(var i = 0; i < rows.length; i++) {
15839 // how many rows should it span..
15842 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15843 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15845 unselectable : "on",
15848 cls: 'fc-event-inner',
15852 // cls: 'fc-event-time',
15853 // html : cells.length > 1 ? '' : ev.time
15857 cls: 'fc-event-title',
15858 html : String.format('{0}', ev.title)
15865 cls: 'ui-resizable-handle ui-resizable-e',
15866 html : '  '
15873 cfg.cls += ' fc-event-start';
15875 if ((i+1) == rows.length) {
15876 cfg.cls += ' fc-event-end';
15879 var ctr = _this.el.select('.fc-event-container',true).first();
15880 var cg = ctr.createChild(cfg);
15882 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15883 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15885 var r = (c.more.length) ? 1 : 0;
15886 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15887 cg.setWidth(ebox.right - sbox.x -2);
15889 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15890 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15891 cg.on('click', _this.onEventClick, _this, ev);
15902 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15903 style : 'position: absolute',
15904 unselectable : "on",
15907 cls: 'fc-event-inner',
15911 cls: 'fc-event-title',
15919 cls: 'ui-resizable-handle ui-resizable-e',
15920 html : '  '
15926 var ctr = _this.el.select('.fc-event-container',true).first();
15927 var cg = ctr.createChild(cfg);
15929 var sbox = c.select('.fc-day-content',true).first().getBox();
15930 var ebox = c.select('.fc-day-content',true).first().getBox();
15932 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15933 cg.setWidth(ebox.right - sbox.x -2);
15935 cg.on('click', _this.onMoreEventClick, _this, c.more);
15945 onEventEnter: function (e, el,event,d) {
15946 this.fireEvent('evententer', this, el, event);
15949 onEventLeave: function (e, el,event,d) {
15950 this.fireEvent('eventleave', this, el, event);
15953 onEventClick: function (e, el,event,d) {
15954 this.fireEvent('eventclick', this, el, event);
15957 onMonthChange: function () {
15961 onMoreEventClick: function(e, el, more)
15965 this.calpopover.placement = 'right';
15966 this.calpopover.setTitle('More');
15968 this.calpopover.setContent('');
15970 var ctr = this.calpopover.el.select('.popover-content', true).first();
15972 Roo.each(more, function(m){
15974 cls : 'fc-event-hori fc-event-draggable',
15977 var cg = ctr.createChild(cfg);
15979 cg.on('click', _this.onEventClick, _this, m);
15982 this.calpopover.show(el);
15987 onLoad: function ()
15989 this.calevents = [];
15992 if(this.store.getCount() > 0){
15993 this.store.data.each(function(d){
15996 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15997 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15998 time : d.data.start_time,
15999 title : d.data.title,
16000 description : d.data.description,
16001 venue : d.data.venue
16006 this.renderEvents();
16008 if(this.calevents.length && this.loadMask){
16009 this.maskEl.hide();
16013 onBeforeLoad: function()
16015 this.clearEvents();
16017 this.maskEl.show();
16031 * @class Roo.bootstrap.Popover
16032 * @extends Roo.bootstrap.Component
16033 * Bootstrap Popover class
16034 * @cfg {String} html contents of the popover (or false to use children..)
16035 * @cfg {String} title of popover (or false to hide)
16036 * @cfg {String} placement how it is placed
16037 * @cfg {String} trigger click || hover (or false to trigger manually)
16038 * @cfg {String} over what (parent or false to trigger manually.)
16039 * @cfg {Number} delay - delay before showing
16042 * Create a new Popover
16043 * @param {Object} config The config object
16046 Roo.bootstrap.Popover = function(config){
16047 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16053 * After the popover show
16055 * @param {Roo.bootstrap.Popover} this
16060 * After the popover hide
16062 * @param {Roo.bootstrap.Popover} this
16068 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16070 title: 'Fill in a title',
16073 placement : 'right',
16074 trigger : 'hover', // hover
16080 can_build_overlaid : false,
16082 getChildContainer : function()
16084 return this.el.select('.popover-content',true).first();
16087 getAutoCreate : function(){
16090 cls : 'popover roo-dynamic',
16091 style: 'display:block',
16097 cls : 'popover-inner',
16101 cls: 'popover-title',
16105 cls : 'popover-content',
16116 setTitle: function(str)
16119 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16121 setContent: function(str)
16124 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16126 // as it get's added to the bottom of the page.
16127 onRender : function(ct, position)
16129 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16131 var cfg = Roo.apply({}, this.getAutoCreate());
16135 cfg.cls += ' ' + this.cls;
16138 cfg.style = this.style;
16140 //Roo.log("adding to ");
16141 this.el = Roo.get(document.body).createChild(cfg, position);
16142 // Roo.log(this.el);
16147 initEvents : function()
16149 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16150 this.el.enableDisplayMode('block');
16152 if (this.over === false) {
16155 if (this.triggers === false) {
16158 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16159 var triggers = this.trigger ? this.trigger.split(' ') : [];
16160 Roo.each(triggers, function(trigger) {
16162 if (trigger == 'click') {
16163 on_el.on('click', this.toggle, this);
16164 } else if (trigger != 'manual') {
16165 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16166 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16168 on_el.on(eventIn ,this.enter, this);
16169 on_el.on(eventOut, this.leave, this);
16180 toggle : function () {
16181 this.hoverState == 'in' ? this.leave() : this.enter();
16184 enter : function () {
16186 clearTimeout(this.timeout);
16188 this.hoverState = 'in';
16190 if (!this.delay || !this.delay.show) {
16195 this.timeout = setTimeout(function () {
16196 if (_t.hoverState == 'in') {
16199 }, this.delay.show)
16202 leave : function() {
16203 clearTimeout(this.timeout);
16205 this.hoverState = 'out';
16207 if (!this.delay || !this.delay.hide) {
16212 this.timeout = setTimeout(function () {
16213 if (_t.hoverState == 'out') {
16216 }, this.delay.hide)
16219 show : function (on_el)
16222 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16226 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16227 if (this.html !== false) {
16228 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16230 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16231 if (!this.title.length) {
16232 this.el.select('.popover-title',true).hide();
16235 var placement = typeof this.placement == 'function' ?
16236 this.placement.call(this, this.el, on_el) :
16239 var autoToken = /\s?auto?\s?/i;
16240 var autoPlace = autoToken.test(placement);
16242 placement = placement.replace(autoToken, '') || 'top';
16246 //this.el.setXY([0,0]);
16248 this.el.dom.style.display='block';
16249 this.el.addClass(placement);
16251 //this.el.appendTo(on_el);
16253 var p = this.getPosition();
16254 var box = this.el.getBox();
16259 var align = Roo.bootstrap.Popover.alignment[placement];
16260 this.el.alignTo(on_el, align[0],align[1]);
16261 //var arrow = this.el.select('.arrow',true).first();
16262 //arrow.set(align[2],
16264 this.el.addClass('in');
16267 if (this.el.hasClass('fade')) {
16271 this.hoverState = 'in';
16273 this.fireEvent('show', this);
16278 this.el.setXY([0,0]);
16279 this.el.removeClass('in');
16281 this.hoverState = null;
16283 this.fireEvent('hide', this);
16288 Roo.bootstrap.Popover.alignment = {
16289 'left' : ['r-l', [-10,0], 'right'],
16290 'right' : ['l-r', [10,0], 'left'],
16291 'bottom' : ['t-b', [0,10], 'top'],
16292 'top' : [ 'b-t', [0,-10], 'bottom']
16303 * @class Roo.bootstrap.Progress
16304 * @extends Roo.bootstrap.Component
16305 * Bootstrap Progress class
16306 * @cfg {Boolean} striped striped of the progress bar
16307 * @cfg {Boolean} active animated of the progress bar
16311 * Create a new Progress
16312 * @param {Object} config The config object
16315 Roo.bootstrap.Progress = function(config){
16316 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16319 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16324 getAutoCreate : function(){
16332 cfg.cls += ' progress-striped';
16336 cfg.cls += ' active';
16355 * @class Roo.bootstrap.ProgressBar
16356 * @extends Roo.bootstrap.Component
16357 * Bootstrap ProgressBar class
16358 * @cfg {Number} aria_valuenow aria-value now
16359 * @cfg {Number} aria_valuemin aria-value min
16360 * @cfg {Number} aria_valuemax aria-value max
16361 * @cfg {String} label label for the progress bar
16362 * @cfg {String} panel (success | info | warning | danger )
16363 * @cfg {String} role role of the progress bar
16364 * @cfg {String} sr_only text
16368 * Create a new ProgressBar
16369 * @param {Object} config The config object
16372 Roo.bootstrap.ProgressBar = function(config){
16373 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16376 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16380 aria_valuemax : 100,
16386 getAutoCreate : function()
16391 cls: 'progress-bar',
16392 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16404 cfg.role = this.role;
16407 if(this.aria_valuenow){
16408 cfg['aria-valuenow'] = this.aria_valuenow;
16411 if(this.aria_valuemin){
16412 cfg['aria-valuemin'] = this.aria_valuemin;
16415 if(this.aria_valuemax){
16416 cfg['aria-valuemax'] = this.aria_valuemax;
16419 if(this.label && !this.sr_only){
16420 cfg.html = this.label;
16424 cfg.cls += ' progress-bar-' + this.panel;
16430 update : function(aria_valuenow)
16432 this.aria_valuenow = aria_valuenow;
16434 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16449 * @class Roo.bootstrap.TabGroup
16450 * @extends Roo.bootstrap.Column
16451 * Bootstrap Column class
16452 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16453 * @cfg {Boolean} carousel true to make the group behave like a carousel
16454 * @cfg {Boolean} bullets show bullets for the panels
16455 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16456 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16457 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16458 * @cfg {Boolean} showarrow (true|false) show arrow default true
16461 * Create a new TabGroup
16462 * @param {Object} config The config object
16465 Roo.bootstrap.TabGroup = function(config){
16466 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16468 this.navId = Roo.id();
16471 Roo.bootstrap.TabGroup.register(this);
16475 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16478 transition : false,
16483 slideOnTouch : false,
16486 getAutoCreate : function()
16488 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16490 cfg.cls += ' tab-content';
16492 if (this.carousel) {
16493 cfg.cls += ' carousel slide';
16496 cls : 'carousel-inner',
16500 if(this.bullets && !Roo.isTouch){
16503 cls : 'carousel-bullets',
16507 if(this.bullets_cls){
16508 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16515 cfg.cn[0].cn.push(bullets);
16518 if(this.showarrow){
16519 cfg.cn[0].cn.push({
16521 class : 'carousel-arrow',
16525 class : 'carousel-prev',
16529 class : 'fa fa-chevron-left'
16535 class : 'carousel-next',
16539 class : 'fa fa-chevron-right'
16552 initEvents: function()
16554 if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16555 this.el.on("touchstart", this.onTouchStart, this);
16558 if(this.autoslide){
16561 this.slideFn = window.setInterval(function() {
16562 _this.showPanelNext();
16566 if(this.showarrow){
16567 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16568 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16574 onTouchStart : function(e, el, o)
16576 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16580 this.showPanelNext();
16583 getChildContainer : function()
16585 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16589 * register a Navigation item
16590 * @param {Roo.bootstrap.NavItem} the navitem to add
16592 register : function(item)
16594 this.tabs.push( item);
16595 item.navId = this.navId; // not really needed..
16600 getActivePanel : function()
16603 Roo.each(this.tabs, function(t) {
16613 getPanelByName : function(n)
16616 Roo.each(this.tabs, function(t) {
16617 if (t.tabId == n) {
16625 indexOfPanel : function(p)
16628 Roo.each(this.tabs, function(t,i) {
16629 if (t.tabId == p.tabId) {
16638 * show a specific panel
16639 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16640 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16642 showPanel : function (pan)
16644 if(this.transition || typeof(pan) == 'undefined'){
16645 Roo.log("waiting for the transitionend");
16649 if (typeof(pan) == 'number') {
16650 pan = this.tabs[pan];
16653 if (typeof(pan) == 'string') {
16654 pan = this.getPanelByName(pan);
16657 var cur = this.getActivePanel();
16660 Roo.log('pan or acitve pan is undefined');
16664 if (pan.tabId == this.getActivePanel().tabId) {
16668 if (false === cur.fireEvent('beforedeactivate')) {
16672 if(this.bullets > 0 && !Roo.isTouch){
16673 this.setActiveBullet(this.indexOfPanel(pan));
16676 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16678 this.transition = true;
16679 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16680 var lr = dir == 'next' ? 'left' : 'right';
16681 pan.el.addClass(dir); // or prev
16682 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16683 cur.el.addClass(lr); // or right
16684 pan.el.addClass(lr);
16687 cur.el.on('transitionend', function() {
16688 Roo.log("trans end?");
16690 pan.el.removeClass([lr,dir]);
16691 pan.setActive(true);
16693 cur.el.removeClass([lr]);
16694 cur.setActive(false);
16696 _this.transition = false;
16698 }, this, { single: true } );
16703 cur.setActive(false);
16704 pan.setActive(true);
16709 showPanelNext : function()
16711 var i = this.indexOfPanel(this.getActivePanel());
16713 if (i >= this.tabs.length - 1 && !this.autoslide) {
16717 if (i >= this.tabs.length - 1 && this.autoslide) {
16721 this.showPanel(this.tabs[i+1]);
16724 showPanelPrev : function()
16726 var i = this.indexOfPanel(this.getActivePanel());
16728 if (i < 1 && !this.autoslide) {
16732 if (i < 1 && this.autoslide) {
16733 i = this.tabs.length;
16736 this.showPanel(this.tabs[i-1]);
16740 addBullet: function()
16742 if(!this.bullets || Roo.isTouch){
16745 var ctr = this.el.select('.carousel-bullets',true).first();
16746 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16747 var bullet = ctr.createChild({
16748 cls : 'bullet bullet-' + i
16749 },ctr.dom.lastChild);
16754 bullet.on('click', (function(e, el, o, ii, t){
16756 e.preventDefault();
16758 this.showPanel(ii);
16760 if(this.autoslide && this.slideFn){
16761 clearInterval(this.slideFn);
16762 this.slideFn = window.setInterval(function() {
16763 _this.showPanelNext();
16767 }).createDelegate(this, [i, bullet], true));
16772 setActiveBullet : function(i)
16778 Roo.each(this.el.select('.bullet', true).elements, function(el){
16779 el.removeClass('selected');
16782 var bullet = this.el.select('.bullet-' + i, true).first();
16788 bullet.addClass('selected');
16799 Roo.apply(Roo.bootstrap.TabGroup, {
16803 * register a Navigation Group
16804 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16806 register : function(navgrp)
16808 this.groups[navgrp.navId] = navgrp;
16812 * fetch a Navigation Group based on the navigation ID
16813 * if one does not exist , it will get created.
16814 * @param {string} the navgroup to add
16815 * @returns {Roo.bootstrap.NavGroup} the navgroup
16817 get: function(navId) {
16818 if (typeof(this.groups[navId]) == 'undefined') {
16819 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16821 return this.groups[navId] ;
16836 * @class Roo.bootstrap.TabPanel
16837 * @extends Roo.bootstrap.Component
16838 * Bootstrap TabPanel class
16839 * @cfg {Boolean} active panel active
16840 * @cfg {String} html panel content
16841 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16842 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16846 * Create a new TabPanel
16847 * @param {Object} config The config object
16850 Roo.bootstrap.TabPanel = function(config){
16851 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16855 * Fires when the active status changes
16856 * @param {Roo.bootstrap.TabPanel} this
16857 * @param {Boolean} state the new state
16862 * @event beforedeactivate
16863 * Fires before a tab is de-activated - can be used to do validation on a form.
16864 * @param {Roo.bootstrap.TabPanel} this
16865 * @return {Boolean} false if there is an error
16868 'beforedeactivate': true
16871 this.tabId = this.tabId || Roo.id();
16875 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16882 getAutoCreate : function(){
16885 // item is needed for carousel - not sure if it has any effect otherwise
16886 cls: 'tab-pane item',
16887 html: this.html || ''
16891 cfg.cls += ' active';
16895 cfg.tabId = this.tabId;
16902 initEvents: function()
16904 var p = this.parent();
16905 this.navId = this.navId || p.navId;
16907 if (typeof(this.navId) != 'undefined') {
16908 // not really needed.. but just in case.. parent should be a NavGroup.
16909 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16913 var i = tg.tabs.length - 1;
16915 if(this.active && tg.bullets > 0 && i < tg.bullets){
16916 tg.setActiveBullet(i);
16923 onRender : function(ct, position)
16925 // Roo.log("Call onRender: " + this.xtype);
16927 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16935 setActive: function(state)
16937 Roo.log("panel - set active " + this.tabId + "=" + state);
16939 this.active = state;
16941 this.el.removeClass('active');
16943 } else if (!this.el.hasClass('active')) {
16944 this.el.addClass('active');
16947 this.fireEvent('changed', this, state);
16964 * @class Roo.bootstrap.DateField
16965 * @extends Roo.bootstrap.Input
16966 * Bootstrap DateField class
16967 * @cfg {Number} weekStart default 0
16968 * @cfg {String} viewMode default empty, (months|years)
16969 * @cfg {String} minViewMode default empty, (months|years)
16970 * @cfg {Number} startDate default -Infinity
16971 * @cfg {Number} endDate default Infinity
16972 * @cfg {Boolean} todayHighlight default false
16973 * @cfg {Boolean} todayBtn default false
16974 * @cfg {Boolean} calendarWeeks default false
16975 * @cfg {Object} daysOfWeekDisabled default empty
16976 * @cfg {Boolean} singleMode default false (true | false)
16978 * @cfg {Boolean} keyboardNavigation default true
16979 * @cfg {String} language default en
16982 * Create a new DateField
16983 * @param {Object} config The config object
16986 Roo.bootstrap.DateField = function(config){
16987 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16991 * Fires when this field show.
16992 * @param {Roo.bootstrap.DateField} this
16993 * @param {Mixed} date The date value
16998 * Fires when this field hide.
16999 * @param {Roo.bootstrap.DateField} this
17000 * @param {Mixed} date The date value
17005 * Fires when select a date.
17006 * @param {Roo.bootstrap.DateField} this
17007 * @param {Mixed} date The date value
17011 * @event beforeselect
17012 * Fires when before select a date.
17013 * @param {Roo.bootstrap.DateField} this
17014 * @param {Mixed} date The date value
17016 beforeselect : true
17020 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17023 * @cfg {String} format
17024 * The default date format string which can be overriden for localization support. The format must be
17025 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17029 * @cfg {String} altFormats
17030 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17031 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17033 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17041 todayHighlight : false,
17047 keyboardNavigation: true,
17049 calendarWeeks: false,
17051 startDate: -Infinity,
17055 daysOfWeekDisabled: [],
17059 singleMode : false,
17061 UTCDate: function()
17063 return new Date(Date.UTC.apply(Date, arguments));
17066 UTCToday: function()
17068 var today = new Date();
17069 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17072 getDate: function() {
17073 var d = this.getUTCDate();
17074 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17077 getUTCDate: function() {
17081 setDate: function(d) {
17082 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17085 setUTCDate: function(d) {
17087 this.setValue(this.formatDate(this.date));
17090 onRender: function(ct, position)
17093 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17095 this.language = this.language || 'en';
17096 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17097 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17099 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17100 this.format = this.format || 'm/d/y';
17101 this.isInline = false;
17102 this.isInput = true;
17103 this.component = this.el.select('.add-on', true).first() || false;
17104 this.component = (this.component && this.component.length === 0) ? false : this.component;
17105 this.hasInput = this.component && this.inputEL().length;
17107 if (typeof(this.minViewMode === 'string')) {
17108 switch (this.minViewMode) {
17110 this.minViewMode = 1;
17113 this.minViewMode = 2;
17116 this.minViewMode = 0;
17121 if (typeof(this.viewMode === 'string')) {
17122 switch (this.viewMode) {
17135 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17137 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17139 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17141 this.picker().on('mousedown', this.onMousedown, this);
17142 this.picker().on('click', this.onClick, this);
17144 this.picker().addClass('datepicker-dropdown');
17146 this.startViewMode = this.viewMode;
17148 if(this.singleMode){
17149 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17150 v.setVisibilityMode(Roo.Element.DISPLAY);
17154 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17155 v.setStyle('width', '189px');
17159 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17160 if(!this.calendarWeeks){
17165 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17166 v.attr('colspan', function(i, val){
17167 return parseInt(val) + 1;
17172 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17174 this.setStartDate(this.startDate);
17175 this.setEndDate(this.endDate);
17177 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17184 if(this.isInline) {
17189 picker : function()
17191 return this.pickerEl;
17192 // return this.el.select('.datepicker', true).first();
17195 fillDow: function()
17197 var dowCnt = this.weekStart;
17206 if(this.calendarWeeks){
17214 while (dowCnt < this.weekStart + 7) {
17218 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17222 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17225 fillMonths: function()
17228 var months = this.picker().select('>.datepicker-months td', true).first();
17230 months.dom.innerHTML = '';
17236 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17239 months.createChild(month);
17246 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;
17248 if (this.date < this.startDate) {
17249 this.viewDate = new Date(this.startDate);
17250 } else if (this.date > this.endDate) {
17251 this.viewDate = new Date(this.endDate);
17253 this.viewDate = new Date(this.date);
17261 var d = new Date(this.viewDate),
17262 year = d.getUTCFullYear(),
17263 month = d.getUTCMonth(),
17264 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17265 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17266 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17267 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17268 currentDate = this.date && this.date.valueOf(),
17269 today = this.UTCToday();
17271 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17273 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17275 // this.picker.select('>tfoot th.today').
17276 // .text(dates[this.language].today)
17277 // .toggle(this.todayBtn !== false);
17279 this.updateNavArrows();
17282 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17284 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17286 prevMonth.setUTCDate(day);
17288 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17290 var nextMonth = new Date(prevMonth);
17292 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17294 nextMonth = nextMonth.valueOf();
17296 var fillMonths = false;
17298 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17300 while(prevMonth.valueOf() < nextMonth) {
17303 if (prevMonth.getUTCDay() === this.weekStart) {
17305 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17313 if(this.calendarWeeks){
17314 // ISO 8601: First week contains first thursday.
17315 // ISO also states week starts on Monday, but we can be more abstract here.
17317 // Start of current week: based on weekstart/current date
17318 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17319 // Thursday of this week
17320 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17321 // First Thursday of year, year from thursday
17322 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17323 // Calendar week: ms between thursdays, div ms per day, div 7 days
17324 calWeek = (th - yth) / 864e5 / 7 + 1;
17326 fillMonths.cn.push({
17334 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17336 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17339 if (this.todayHighlight &&
17340 prevMonth.getUTCFullYear() == today.getFullYear() &&
17341 prevMonth.getUTCMonth() == today.getMonth() &&
17342 prevMonth.getUTCDate() == today.getDate()) {
17343 clsName += ' today';
17346 if (currentDate && prevMonth.valueOf() === currentDate) {
17347 clsName += ' active';
17350 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17351 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17352 clsName += ' disabled';
17355 fillMonths.cn.push({
17357 cls: 'day ' + clsName,
17358 html: prevMonth.getDate()
17361 prevMonth.setDate(prevMonth.getDate()+1);
17364 var currentYear = this.date && this.date.getUTCFullYear();
17365 var currentMonth = this.date && this.date.getUTCMonth();
17367 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17369 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17370 v.removeClass('active');
17372 if(currentYear === year && k === currentMonth){
17373 v.addClass('active');
17376 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17377 v.addClass('disabled');
17383 year = parseInt(year/10, 10) * 10;
17385 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17387 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17390 for (var i = -1; i < 11; i++) {
17391 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17393 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17401 showMode: function(dir)
17404 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17407 Roo.each(this.picker().select('>div',true).elements, function(v){
17408 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17411 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17416 if(this.isInline) {
17420 this.picker().removeClass(['bottom', 'top']);
17422 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17424 * place to the top of element!
17428 this.picker().addClass('top');
17429 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17434 this.picker().addClass('bottom');
17436 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17439 parseDate : function(value)
17441 if(!value || value instanceof Date){
17444 var v = Date.parseDate(value, this.format);
17445 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17446 v = Date.parseDate(value, 'Y-m-d');
17448 if(!v && this.altFormats){
17449 if(!this.altFormatsArray){
17450 this.altFormatsArray = this.altFormats.split("|");
17452 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17453 v = Date.parseDate(value, this.altFormatsArray[i]);
17459 formatDate : function(date, fmt)
17461 return (!date || !(date instanceof Date)) ?
17462 date : date.dateFormat(fmt || this.format);
17465 onFocus : function()
17467 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17471 onBlur : function()
17473 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17475 var d = this.inputEl().getValue();
17484 this.picker().show();
17488 this.fireEvent('show', this, this.date);
17493 if(this.isInline) {
17496 this.picker().hide();
17497 this.viewMode = this.startViewMode;
17500 this.fireEvent('hide', this, this.date);
17504 onMousedown: function(e)
17506 e.stopPropagation();
17507 e.preventDefault();
17512 Roo.bootstrap.DateField.superclass.keyup.call(this);
17516 setValue: function(v)
17518 if(this.fireEvent('beforeselect', this, v) !== false){
17519 var d = new Date(this.parseDate(v) ).clearTime();
17521 if(isNaN(d.getTime())){
17522 this.date = this.viewDate = '';
17523 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17527 v = this.formatDate(d);
17529 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17531 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17535 this.fireEvent('select', this, this.date);
17539 getValue: function()
17541 return this.formatDate(this.date);
17544 fireKey: function(e)
17546 if (!this.picker().isVisible()){
17547 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17553 var dateChanged = false,
17555 newDate, newViewDate;
17560 e.preventDefault();
17564 if (!this.keyboardNavigation) {
17567 dir = e.keyCode == 37 ? -1 : 1;
17570 newDate = this.moveYear(this.date, dir);
17571 newViewDate = this.moveYear(this.viewDate, dir);
17572 } else if (e.shiftKey){
17573 newDate = this.moveMonth(this.date, dir);
17574 newViewDate = this.moveMonth(this.viewDate, dir);
17576 newDate = new Date(this.date);
17577 newDate.setUTCDate(this.date.getUTCDate() + dir);
17578 newViewDate = new Date(this.viewDate);
17579 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17581 if (this.dateWithinRange(newDate)){
17582 this.date = newDate;
17583 this.viewDate = newViewDate;
17584 this.setValue(this.formatDate(this.date));
17586 e.preventDefault();
17587 dateChanged = true;
17592 if (!this.keyboardNavigation) {
17595 dir = e.keyCode == 38 ? -1 : 1;
17597 newDate = this.moveYear(this.date, dir);
17598 newViewDate = this.moveYear(this.viewDate, dir);
17599 } else if (e.shiftKey){
17600 newDate = this.moveMonth(this.date, dir);
17601 newViewDate = this.moveMonth(this.viewDate, dir);
17603 newDate = new Date(this.date);
17604 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17605 newViewDate = new Date(this.viewDate);
17606 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17608 if (this.dateWithinRange(newDate)){
17609 this.date = newDate;
17610 this.viewDate = newViewDate;
17611 this.setValue(this.formatDate(this.date));
17613 e.preventDefault();
17614 dateChanged = true;
17618 this.setValue(this.formatDate(this.date));
17620 e.preventDefault();
17623 this.setValue(this.formatDate(this.date));
17637 onClick: function(e)
17639 e.stopPropagation();
17640 e.preventDefault();
17642 var target = e.getTarget();
17644 if(target.nodeName.toLowerCase() === 'i'){
17645 target = Roo.get(target).dom.parentNode;
17648 var nodeName = target.nodeName;
17649 var className = target.className;
17650 var html = target.innerHTML;
17651 //Roo.log(nodeName);
17653 switch(nodeName.toLowerCase()) {
17655 switch(className) {
17661 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17662 switch(this.viewMode){
17664 this.viewDate = this.moveMonth(this.viewDate, dir);
17668 this.viewDate = this.moveYear(this.viewDate, dir);
17674 var date = new Date();
17675 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17677 this.setValue(this.formatDate(this.date));
17684 if (className.indexOf('disabled') < 0) {
17685 this.viewDate.setUTCDate(1);
17686 if (className.indexOf('month') > -1) {
17687 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17689 var year = parseInt(html, 10) || 0;
17690 this.viewDate.setUTCFullYear(year);
17694 if(this.singleMode){
17695 this.setValue(this.formatDate(this.viewDate));
17706 //Roo.log(className);
17707 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17708 var day = parseInt(html, 10) || 1;
17709 var year = this.viewDate.getUTCFullYear(),
17710 month = this.viewDate.getUTCMonth();
17712 if (className.indexOf('old') > -1) {
17719 } else if (className.indexOf('new') > -1) {
17727 //Roo.log([year,month,day]);
17728 this.date = this.UTCDate(year, month, day,0,0,0,0);
17729 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17731 //Roo.log(this.formatDate(this.date));
17732 this.setValue(this.formatDate(this.date));
17739 setStartDate: function(startDate)
17741 this.startDate = startDate || -Infinity;
17742 if (this.startDate !== -Infinity) {
17743 this.startDate = this.parseDate(this.startDate);
17746 this.updateNavArrows();
17749 setEndDate: function(endDate)
17751 this.endDate = endDate || Infinity;
17752 if (this.endDate !== Infinity) {
17753 this.endDate = this.parseDate(this.endDate);
17756 this.updateNavArrows();
17759 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17761 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17762 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17763 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17765 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17766 return parseInt(d, 10);
17769 this.updateNavArrows();
17772 updateNavArrows: function()
17774 if(this.singleMode){
17778 var d = new Date(this.viewDate),
17779 year = d.getUTCFullYear(),
17780 month = d.getUTCMonth();
17782 Roo.each(this.picker().select('.prev', true).elements, function(v){
17784 switch (this.viewMode) {
17787 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17793 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17800 Roo.each(this.picker().select('.next', true).elements, function(v){
17802 switch (this.viewMode) {
17805 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17811 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17819 moveMonth: function(date, dir)
17824 var new_date = new Date(date.valueOf()),
17825 day = new_date.getUTCDate(),
17826 month = new_date.getUTCMonth(),
17827 mag = Math.abs(dir),
17829 dir = dir > 0 ? 1 : -1;
17832 // If going back one month, make sure month is not current month
17833 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17835 return new_date.getUTCMonth() == month;
17837 // If going forward one month, make sure month is as expected
17838 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17840 return new_date.getUTCMonth() != new_month;
17842 new_month = month + dir;
17843 new_date.setUTCMonth(new_month);
17844 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17845 if (new_month < 0 || new_month > 11) {
17846 new_month = (new_month + 12) % 12;
17849 // For magnitudes >1, move one month at a time...
17850 for (var i=0; i<mag; i++) {
17851 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17852 new_date = this.moveMonth(new_date, dir);
17854 // ...then reset the day, keeping it in the new month
17855 new_month = new_date.getUTCMonth();
17856 new_date.setUTCDate(day);
17858 return new_month != new_date.getUTCMonth();
17861 // Common date-resetting loop -- if date is beyond end of month, make it
17864 new_date.setUTCDate(--day);
17865 new_date.setUTCMonth(new_month);
17870 moveYear: function(date, dir)
17872 return this.moveMonth(date, dir*12);
17875 dateWithinRange: function(date)
17877 return date >= this.startDate && date <= this.endDate;
17883 this.picker().remove();
17888 Roo.apply(Roo.bootstrap.DateField, {
17899 html: '<i class="fa fa-arrow-left"/>'
17909 html: '<i class="fa fa-arrow-right"/>'
17951 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17952 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17953 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17954 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17955 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17968 navFnc: 'FullYear',
17973 navFnc: 'FullYear',
17978 Roo.apply(Roo.bootstrap.DateField, {
17982 cls: 'datepicker dropdown-menu roo-dynamic',
17986 cls: 'datepicker-days',
17990 cls: 'table-condensed',
17992 Roo.bootstrap.DateField.head,
17996 Roo.bootstrap.DateField.footer
18003 cls: 'datepicker-months',
18007 cls: 'table-condensed',
18009 Roo.bootstrap.DateField.head,
18010 Roo.bootstrap.DateField.content,
18011 Roo.bootstrap.DateField.footer
18018 cls: 'datepicker-years',
18022 cls: 'table-condensed',
18024 Roo.bootstrap.DateField.head,
18025 Roo.bootstrap.DateField.content,
18026 Roo.bootstrap.DateField.footer
18045 * @class Roo.bootstrap.TimeField
18046 * @extends Roo.bootstrap.Input
18047 * Bootstrap DateField class
18051 * Create a new TimeField
18052 * @param {Object} config The config object
18055 Roo.bootstrap.TimeField = function(config){
18056 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18060 * Fires when this field show.
18061 * @param {Roo.bootstrap.DateField} thisthis
18062 * @param {Mixed} date The date value
18067 * Fires when this field hide.
18068 * @param {Roo.bootstrap.DateField} this
18069 * @param {Mixed} date The date value
18074 * Fires when select a date.
18075 * @param {Roo.bootstrap.DateField} this
18076 * @param {Mixed} date The date value
18082 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18085 * @cfg {String} format
18086 * The default time format string which can be overriden for localization support. The format must be
18087 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18091 onRender: function(ct, position)
18094 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18096 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18098 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18100 this.pop = this.picker().select('>.datepicker-time',true).first();
18101 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18103 this.picker().on('mousedown', this.onMousedown, this);
18104 this.picker().on('click', this.onClick, this);
18106 this.picker().addClass('datepicker-dropdown');
18111 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18112 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18113 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18114 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18115 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18116 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18120 fireKey: function(e){
18121 if (!this.picker().isVisible()){
18122 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18128 e.preventDefault();
18136 this.onTogglePeriod();
18139 this.onIncrementMinutes();
18142 this.onDecrementMinutes();
18151 onClick: function(e) {
18152 e.stopPropagation();
18153 e.preventDefault();
18156 picker : function()
18158 return this.el.select('.datepicker', true).first();
18161 fillTime: function()
18163 var time = this.pop.select('tbody', true).first();
18165 time.dom.innerHTML = '';
18180 cls: 'hours-up glyphicon glyphicon-chevron-up'
18200 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18221 cls: 'timepicker-hour',
18236 cls: 'timepicker-minute',
18251 cls: 'btn btn-primary period',
18273 cls: 'hours-down glyphicon glyphicon-chevron-down'
18293 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18311 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18318 var hours = this.time.getHours();
18319 var minutes = this.time.getMinutes();
18332 hours = hours - 12;
18336 hours = '0' + hours;
18340 minutes = '0' + minutes;
18343 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18344 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18345 this.pop.select('button', true).first().dom.innerHTML = period;
18351 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18353 var cls = ['bottom'];
18355 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18362 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18367 this.picker().addClass(cls.join('-'));
18371 Roo.each(cls, function(c){
18373 _this.picker().setTop(_this.inputEl().getHeight());
18377 _this.picker().setTop(0 - _this.picker().getHeight());
18382 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18386 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18393 onFocus : function()
18395 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18399 onBlur : function()
18401 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18407 this.picker().show();
18412 this.fireEvent('show', this, this.date);
18417 this.picker().hide();
18420 this.fireEvent('hide', this, this.date);
18423 setTime : function()
18426 this.setValue(this.time.format(this.format));
18428 this.fireEvent('select', this, this.date);
18433 onMousedown: function(e){
18434 e.stopPropagation();
18435 e.preventDefault();
18438 onIncrementHours: function()
18440 Roo.log('onIncrementHours');
18441 this.time = this.time.add(Date.HOUR, 1);
18446 onDecrementHours: function()
18448 Roo.log('onDecrementHours');
18449 this.time = this.time.add(Date.HOUR, -1);
18453 onIncrementMinutes: function()
18455 Roo.log('onIncrementMinutes');
18456 this.time = this.time.add(Date.MINUTE, 1);
18460 onDecrementMinutes: function()
18462 Roo.log('onDecrementMinutes');
18463 this.time = this.time.add(Date.MINUTE, -1);
18467 onTogglePeriod: function()
18469 Roo.log('onTogglePeriod');
18470 this.time = this.time.add(Date.HOUR, 12);
18477 Roo.apply(Roo.bootstrap.TimeField, {
18507 cls: 'btn btn-info ok',
18519 Roo.apply(Roo.bootstrap.TimeField, {
18523 cls: 'datepicker dropdown-menu',
18527 cls: 'datepicker-time',
18531 cls: 'table-condensed',
18533 Roo.bootstrap.TimeField.content,
18534 Roo.bootstrap.TimeField.footer
18553 * @class Roo.bootstrap.MonthField
18554 * @extends Roo.bootstrap.Input
18555 * Bootstrap MonthField class
18557 * @cfg {String} language default en
18560 * Create a new MonthField
18561 * @param {Object} config The config object
18564 Roo.bootstrap.MonthField = function(config){
18565 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18570 * Fires when this field show.
18571 * @param {Roo.bootstrap.MonthField} this
18572 * @param {Mixed} date The date value
18577 * Fires when this field hide.
18578 * @param {Roo.bootstrap.MonthField} this
18579 * @param {Mixed} date The date value
18584 * Fires when select a date.
18585 * @param {Roo.bootstrap.MonthField} this
18586 * @param {String} oldvalue The old value
18587 * @param {String} newvalue The new value
18593 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18595 onRender: function(ct, position)
18598 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18600 this.language = this.language || 'en';
18601 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18602 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18604 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18605 this.isInline = false;
18606 this.isInput = true;
18607 this.component = this.el.select('.add-on', true).first() || false;
18608 this.component = (this.component && this.component.length === 0) ? false : this.component;
18609 this.hasInput = this.component && this.inputEL().length;
18611 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18613 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18615 this.picker().on('mousedown', this.onMousedown, this);
18616 this.picker().on('click', this.onClick, this);
18618 this.picker().addClass('datepicker-dropdown');
18620 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18621 v.setStyle('width', '189px');
18628 if(this.isInline) {
18634 setValue: function(v, suppressEvent)
18636 var o = this.getValue();
18638 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18642 if(suppressEvent !== true){
18643 this.fireEvent('select', this, o, v);
18648 getValue: function()
18653 onClick: function(e)
18655 e.stopPropagation();
18656 e.preventDefault();
18658 var target = e.getTarget();
18660 if(target.nodeName.toLowerCase() === 'i'){
18661 target = Roo.get(target).dom.parentNode;
18664 var nodeName = target.nodeName;
18665 var className = target.className;
18666 var html = target.innerHTML;
18668 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18672 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18674 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18680 picker : function()
18682 return this.pickerEl;
18685 fillMonths: function()
18688 var months = this.picker().select('>.datepicker-months td', true).first();
18690 months.dom.innerHTML = '';
18696 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18699 months.createChild(month);
18708 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18709 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18712 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18713 e.removeClass('active');
18715 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18716 e.addClass('active');
18723 if(this.isInline) {
18727 this.picker().removeClass(['bottom', 'top']);
18729 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18731 * place to the top of element!
18735 this.picker().addClass('top');
18736 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18741 this.picker().addClass('bottom');
18743 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18746 onFocus : function()
18748 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18752 onBlur : function()
18754 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18756 var d = this.inputEl().getValue();
18765 this.picker().show();
18766 this.picker().select('>.datepicker-months', true).first().show();
18770 this.fireEvent('show', this, this.date);
18775 if(this.isInline) {
18778 this.picker().hide();
18779 this.fireEvent('hide', this, this.date);
18783 onMousedown: function(e)
18785 e.stopPropagation();
18786 e.preventDefault();
18791 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18795 fireKey: function(e)
18797 if (!this.picker().isVisible()){
18798 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18809 e.preventDefault();
18813 dir = e.keyCode == 37 ? -1 : 1;
18815 this.vIndex = this.vIndex + dir;
18817 if(this.vIndex < 0){
18821 if(this.vIndex > 11){
18825 if(isNaN(this.vIndex)){
18829 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18835 dir = e.keyCode == 38 ? -1 : 1;
18837 this.vIndex = this.vIndex + dir * 4;
18839 if(this.vIndex < 0){
18843 if(this.vIndex > 11){
18847 if(isNaN(this.vIndex)){
18851 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18856 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18857 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18861 e.preventDefault();
18864 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18865 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18881 this.picker().remove();
18886 Roo.apply(Roo.bootstrap.MonthField, {
18905 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18906 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18911 Roo.apply(Roo.bootstrap.MonthField, {
18915 cls: 'datepicker dropdown-menu roo-dynamic',
18919 cls: 'datepicker-months',
18923 cls: 'table-condensed',
18925 Roo.bootstrap.DateField.content
18945 * @class Roo.bootstrap.CheckBox
18946 * @extends Roo.bootstrap.Input
18947 * Bootstrap CheckBox class
18949 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18950 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18951 * @cfg {String} boxLabel The text that appears beside the checkbox
18952 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18953 * @cfg {Boolean} checked initnal the element
18954 * @cfg {Boolean} inline inline the element (default false)
18955 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18958 * Create a new CheckBox
18959 * @param {Object} config The config object
18962 Roo.bootstrap.CheckBox = function(config){
18963 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18968 * Fires when the element is checked or unchecked.
18969 * @param {Roo.bootstrap.CheckBox} this This input
18970 * @param {Boolean} checked The new checked value
18977 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18979 inputType: 'checkbox',
18987 getAutoCreate : function()
18989 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18995 cfg.cls = 'form-group ' + this.inputType; //input-group
18998 cfg.cls += ' ' + this.inputType + '-inline';
19004 type : this.inputType,
19005 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19006 cls : 'roo-' + this.inputType, //'form-box',
19007 placeholder : this.placeholder || ''
19011 if (this.weight) { // Validity check?
19012 cfg.cls += " " + this.inputType + "-" + this.weight;
19015 if (this.disabled) {
19016 input.disabled=true;
19020 input.checked = this.checked;
19024 input.name = this.name;
19028 input.cls += ' input-' + this.size;
19033 ['xs','sm','md','lg'].map(function(size){
19034 if (settings[size]) {
19035 cfg.cls += ' col-' + size + '-' + settings[size];
19039 var inputblock = input;
19041 if (this.before || this.after) {
19044 cls : 'input-group',
19049 inputblock.cn.push({
19051 cls : 'input-group-addon',
19056 inputblock.cn.push(input);
19059 inputblock.cn.push({
19061 cls : 'input-group-addon',
19068 if (align ==='left' && this.fieldLabel.length) {
19069 // Roo.log("left and has label");
19075 cls : 'control-label col-md-' + this.labelWidth,
19076 html : this.fieldLabel
19080 cls : "col-md-" + (12 - this.labelWidth),
19087 } else if ( this.fieldLabel.length) {
19088 // Roo.log(" label");
19092 tag: this.boxLabel ? 'span' : 'label',
19094 cls: 'control-label box-input-label',
19095 //cls : 'input-group-addon',
19096 html : this.fieldLabel
19106 // Roo.log(" no label && no align");
19107 cfg.cn = [ inputblock ] ;
19113 var boxLabelCfg = {
19115 //'for': id, // box label is handled by onclick - so no for...
19117 html: this.boxLabel
19121 boxLabelCfg.tooltip = this.tooltip;
19124 cfg.cn.push(boxLabelCfg);
19134 * return the real input element.
19136 inputEl: function ()
19138 return this.el.select('input.roo-' + this.inputType,true).first();
19141 labelEl: function()
19143 return this.el.select('label.control-label',true).first();
19145 /* depricated... */
19149 return this.labelEl();
19152 boxLabelEl: function()
19154 return this.el.select('label.box-label',true).first();
19157 initEvents : function()
19159 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19161 this.inputEl().on('click', this.onClick, this);
19163 if (this.boxLabel) {
19164 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19167 this.startValue = this.getValue();
19170 Roo.bootstrap.CheckBox.register(this);
19174 onClick : function()
19176 this.setChecked(!this.checked);
19179 setChecked : function(state,suppressEvent)
19181 this.startValue = this.getValue();
19183 if(this.inputType == 'radio'){
19185 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19186 e.dom.checked = false;
19189 this.inputEl().dom.checked = true;
19191 this.inputEl().dom.value = this.inputValue;
19193 if(suppressEvent !== true){
19194 this.fireEvent('check', this, true);
19202 this.checked = state;
19204 this.inputEl().dom.checked = state;
19206 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19208 if(suppressEvent !== true){
19209 this.fireEvent('check', this, state);
19215 getValue : function()
19217 if(this.inputType == 'radio'){
19218 return this.getGroupValue();
19221 return this.inputEl().getValue();
19225 getGroupValue : function()
19227 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19231 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19234 setValue : function(v,suppressEvent)
19236 if(this.inputType == 'radio'){
19237 this.setGroupValue(v, suppressEvent);
19241 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19246 setGroupValue : function(v, suppressEvent)
19248 this.startValue = this.getValue();
19250 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19251 e.dom.checked = false;
19253 if(e.dom.value == v){
19254 e.dom.checked = true;
19258 if(suppressEvent !== true){
19259 this.fireEvent('check', this, true);
19267 validate : function()
19271 (this.inputType == 'radio' && this.validateRadio()) ||
19272 (this.inputType == 'checkbox' && this.validateCheckbox())
19278 this.markInvalid();
19282 validateRadio : function()
19286 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19287 if(!e.dom.checked){
19299 validateCheckbox : function()
19302 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19305 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19313 for(var i in group){
19318 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19325 * Mark this field as valid
19327 markValid : function()
19329 if(this.allowBlank){
19335 this.fireEvent('valid', this);
19337 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19340 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19347 if(this.inputType == 'radio'){
19348 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19349 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19350 e.findParent('.form-group', false, true).addClass(_this.validClass);
19357 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19358 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19362 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19368 for(var i in group){
19369 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19370 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19375 * Mark this field as invalid
19376 * @param {String} msg The validation message
19378 markInvalid : function(msg)
19380 if(this.allowBlank){
19386 this.fireEvent('invalid', this, msg);
19388 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19391 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19395 label.markInvalid();
19398 if(this.inputType == 'radio'){
19399 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19400 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19401 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19408 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19409 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19413 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19419 for(var i in group){
19420 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19421 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19428 Roo.apply(Roo.bootstrap.CheckBox, {
19433 * register a CheckBox Group
19434 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19436 register : function(checkbox)
19438 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19439 this.groups[checkbox.groupId] = {};
19442 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19446 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19450 * fetch a CheckBox Group based on the group ID
19451 * @param {string} the group ID
19452 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19454 get: function(groupId) {
19455 if (typeof(this.groups[groupId]) == 'undefined') {
19459 return this.groups[groupId] ;
19471 *<div class="radio">
19473 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19474 Option one is this and that—be sure to include why it's great
19481 *<label class="radio-inline">fieldLabel</label>
19482 *<label class="radio-inline">
19483 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19491 * @class Roo.bootstrap.Radio
19492 * @extends Roo.bootstrap.CheckBox
19493 * Bootstrap Radio class
19496 * Create a new Radio
19497 * @param {Object} config The config object
19500 Roo.bootstrap.Radio = function(config){
19501 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19505 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19507 inputType: 'radio',
19511 getAutoCreate : function()
19513 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19514 align = align || 'left'; // default...
19521 tag : this.inline ? 'span' : 'div',
19526 var inline = this.inline ? ' radio-inline' : '';
19530 // does not need for, as we wrap the input with it..
19532 cls : 'control-label box-label' + inline,
19535 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19539 //cls : 'control-label' + inline,
19540 html : this.fieldLabel,
19541 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19550 type : this.inputType,
19551 //value : (!this.checked) ? this.valueOff : this.inputValue,
19552 value : this.inputValue,
19554 placeholder : this.placeholder || '' // ?? needed????
19557 if (this.weight) { // Validity check?
19558 input.cls += " radio-" + this.weight;
19560 if (this.disabled) {
19561 input.disabled=true;
19565 input.checked = this.checked;
19569 input.name = this.name;
19573 input.cls += ' input-' + this.size;
19576 //?? can span's inline have a width??
19579 ['xs','sm','md','lg'].map(function(size){
19580 if (settings[size]) {
19581 cfg.cls += ' col-' + size + '-' + settings[size];
19585 var inputblock = input;
19587 if (this.before || this.after) {
19590 cls : 'input-group',
19595 inputblock.cn.push({
19597 cls : 'input-group-addon',
19601 inputblock.cn.push(input);
19603 inputblock.cn.push({
19605 cls : 'input-group-addon',
19613 if (this.fieldLabel && this.fieldLabel.length) {
19614 cfg.cn.push(fieldLabel);
19617 // normal bootstrap puts the input inside the label.
19618 // however with our styled version - it has to go after the input.
19620 //lbl.cn.push(inputblock);
19624 cls: 'radio' + inline,
19631 cfg.cn.push( lblwrap);
19636 html: this.boxLabel
19645 initEvents : function()
19647 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19649 this.inputEl().on('click', this.onClick, this);
19650 if (this.boxLabel) {
19651 //Roo.log('find label');
19652 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19657 inputEl: function ()
19659 return this.el.select('input.roo-radio',true).first();
19661 onClick : function()
19664 this.setChecked(true);
19667 setChecked : function(state,suppressEvent)
19670 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19671 v.dom.checked = false;
19674 Roo.log(this.inputEl().dom);
19675 this.checked = state;
19676 this.inputEl().dom.checked = state;
19678 if(suppressEvent !== true){
19679 this.fireEvent('check', this, state);
19682 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19686 getGroupValue : function()
19689 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19690 if(v.dom.checked == true){
19691 value = v.dom.value;
19699 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19700 * @return {Mixed} value The field value
19702 getValue : function(){
19703 return this.getGroupValue();
19709 //<script type="text/javascript">
19712 * Based Ext JS Library 1.1.1
19713 * Copyright(c) 2006-2007, Ext JS, LLC.
19719 * @class Roo.HtmlEditorCore
19720 * @extends Roo.Component
19721 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19723 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19726 Roo.HtmlEditorCore = function(config){
19729 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19734 * @event initialize
19735 * Fires when the editor is fully initialized (including the iframe)
19736 * @param {Roo.HtmlEditorCore} this
19741 * Fires when the editor is first receives the focus. Any insertion must wait
19742 * until after this event.
19743 * @param {Roo.HtmlEditorCore} this
19747 * @event beforesync
19748 * Fires before the textarea is updated with content from the editor iframe. Return false
19749 * to cancel the sync.
19750 * @param {Roo.HtmlEditorCore} this
19751 * @param {String} html
19755 * @event beforepush
19756 * Fires before the iframe editor is updated with content from the textarea. Return false
19757 * to cancel the push.
19758 * @param {Roo.HtmlEditorCore} this
19759 * @param {String} html
19764 * Fires when the textarea is updated with content from the editor iframe.
19765 * @param {Roo.HtmlEditorCore} this
19766 * @param {String} html
19771 * Fires when the iframe editor is updated with content from the textarea.
19772 * @param {Roo.HtmlEditorCore} this
19773 * @param {String} html
19778 * @event editorevent
19779 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19780 * @param {Roo.HtmlEditorCore} this
19786 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19788 // defaults : white / black...
19789 this.applyBlacklists();
19796 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19800 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19806 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19811 * @cfg {Number} height (in pixels)
19815 * @cfg {Number} width (in pixels)
19820 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19823 stylesheets: false,
19828 // private properties
19829 validationEvent : false,
19831 initialized : false,
19833 sourceEditMode : false,
19834 onFocus : Roo.emptyFn,
19836 hideMode:'offsets',
19840 // blacklist + whitelisted elements..
19847 * Protected method that will not generally be called directly. It
19848 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19849 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19851 getDocMarkup : function(){
19855 // inherit styels from page...??
19856 if (this.stylesheets === false) {
19858 Roo.get(document.head).select('style').each(function(node) {
19859 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19862 Roo.get(document.head).select('link').each(function(node) {
19863 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19866 } else if (!this.stylesheets.length) {
19868 st = '<style type="text/css">' +
19869 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19875 st += '<style type="text/css">' +
19876 'IMG { cursor: pointer } ' +
19880 return '<html><head>' + st +
19881 //<style type="text/css">' +
19882 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19884 ' </head><body class="roo-htmleditor-body"></body></html>';
19888 onRender : function(ct, position)
19891 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19892 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19895 this.el.dom.style.border = '0 none';
19896 this.el.dom.setAttribute('tabIndex', -1);
19897 this.el.addClass('x-hidden hide');
19901 if(Roo.isIE){ // fix IE 1px bogus margin
19902 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19906 this.frameId = Roo.id();
19910 var iframe = this.owner.wrap.createChild({
19912 cls: 'form-control', // bootstrap..
19914 name: this.frameId,
19915 frameBorder : 'no',
19916 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19921 this.iframe = iframe.dom;
19923 this.assignDocWin();
19925 this.doc.designMode = 'on';
19928 this.doc.write(this.getDocMarkup());
19932 var task = { // must defer to wait for browser to be ready
19934 //console.log("run task?" + this.doc.readyState);
19935 this.assignDocWin();
19936 if(this.doc.body || this.doc.readyState == 'complete'){
19938 this.doc.designMode="on";
19942 Roo.TaskMgr.stop(task);
19943 this.initEditor.defer(10, this);
19950 Roo.TaskMgr.start(task);
19955 onResize : function(w, h)
19957 Roo.log('resize: ' +w + ',' + h );
19958 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19962 if(typeof w == 'number'){
19964 this.iframe.style.width = w + 'px';
19966 if(typeof h == 'number'){
19968 this.iframe.style.height = h + 'px';
19970 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19977 * Toggles the editor between standard and source edit mode.
19978 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19980 toggleSourceEdit : function(sourceEditMode){
19982 this.sourceEditMode = sourceEditMode === true;
19984 if(this.sourceEditMode){
19986 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19989 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19990 //this.iframe.className = '';
19993 //this.setSize(this.owner.wrap.getSize());
19994 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20001 * Protected method that will not generally be called directly. If you need/want
20002 * custom HTML cleanup, this is the method you should override.
20003 * @param {String} html The HTML to be cleaned
20004 * return {String} The cleaned HTML
20006 cleanHtml : function(html){
20007 html = String(html);
20008 if(html.length > 5){
20009 if(Roo.isSafari){ // strip safari nonsense
20010 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20013 if(html == ' '){
20020 * HTML Editor -> Textarea
20021 * Protected method that will not generally be called directly. Syncs the contents
20022 * of the editor iframe with the textarea.
20024 syncValue : function(){
20025 if(this.initialized){
20026 var bd = (this.doc.body || this.doc.documentElement);
20027 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20028 var html = bd.innerHTML;
20030 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20031 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20033 html = '<div style="'+m[0]+'">' + html + '</div>';
20036 html = this.cleanHtml(html);
20037 // fix up the special chars.. normaly like back quotes in word...
20038 // however we do not want to do this with chinese..
20039 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20040 var cc = b.charCodeAt();
20042 (cc >= 0x4E00 && cc < 0xA000 ) ||
20043 (cc >= 0x3400 && cc < 0x4E00 ) ||
20044 (cc >= 0xf900 && cc < 0xfb00 )
20050 if(this.owner.fireEvent('beforesync', this, html) !== false){
20051 this.el.dom.value = html;
20052 this.owner.fireEvent('sync', this, html);
20058 * Protected method that will not generally be called directly. Pushes the value of the textarea
20059 * into the iframe editor.
20061 pushValue : function(){
20062 if(this.initialized){
20063 var v = this.el.dom.value.trim();
20065 // if(v.length < 1){
20069 if(this.owner.fireEvent('beforepush', this, v) !== false){
20070 var d = (this.doc.body || this.doc.documentElement);
20072 this.cleanUpPaste();
20073 this.el.dom.value = d.innerHTML;
20074 this.owner.fireEvent('push', this, v);
20080 deferFocus : function(){
20081 this.focus.defer(10, this);
20085 focus : function(){
20086 if(this.win && !this.sourceEditMode){
20093 assignDocWin: function()
20095 var iframe = this.iframe;
20098 this.doc = iframe.contentWindow.document;
20099 this.win = iframe.contentWindow;
20101 // if (!Roo.get(this.frameId)) {
20104 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20105 // this.win = Roo.get(this.frameId).dom.contentWindow;
20107 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20111 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20112 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20117 initEditor : function(){
20118 //console.log("INIT EDITOR");
20119 this.assignDocWin();
20123 this.doc.designMode="on";
20125 this.doc.write(this.getDocMarkup());
20128 var dbody = (this.doc.body || this.doc.documentElement);
20129 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20130 // this copies styles from the containing element into thsi one..
20131 // not sure why we need all of this..
20132 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20134 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20135 //ss['background-attachment'] = 'fixed'; // w3c
20136 dbody.bgProperties = 'fixed'; // ie
20137 //Roo.DomHelper.applyStyles(dbody, ss);
20138 Roo.EventManager.on(this.doc, {
20139 //'mousedown': this.onEditorEvent,
20140 'mouseup': this.onEditorEvent,
20141 'dblclick': this.onEditorEvent,
20142 'click': this.onEditorEvent,
20143 'keyup': this.onEditorEvent,
20148 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20150 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20151 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20153 this.initialized = true;
20155 this.owner.fireEvent('initialize', this);
20160 onDestroy : function(){
20166 //for (var i =0; i < this.toolbars.length;i++) {
20167 // // fixme - ask toolbars for heights?
20168 // this.toolbars[i].onDestroy();
20171 //this.wrap.dom.innerHTML = '';
20172 //this.wrap.remove();
20177 onFirstFocus : function(){
20179 this.assignDocWin();
20182 this.activated = true;
20185 if(Roo.isGecko){ // prevent silly gecko errors
20187 var s = this.win.getSelection();
20188 if(!s.focusNode || s.focusNode.nodeType != 3){
20189 var r = s.getRangeAt(0);
20190 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20195 this.execCmd('useCSS', true);
20196 this.execCmd('styleWithCSS', false);
20199 this.owner.fireEvent('activate', this);
20203 adjustFont: function(btn){
20204 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20205 //if(Roo.isSafari){ // safari
20208 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20209 if(Roo.isSafari){ // safari
20210 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20211 v = (v < 10) ? 10 : v;
20212 v = (v > 48) ? 48 : v;
20213 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20218 v = Math.max(1, v+adjust);
20220 this.execCmd('FontSize', v );
20223 onEditorEvent : function(e)
20225 this.owner.fireEvent('editorevent', this, e);
20226 // this.updateToolbar();
20227 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20230 insertTag : function(tg)
20232 // could be a bit smarter... -> wrap the current selected tRoo..
20233 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20235 range = this.createRange(this.getSelection());
20236 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20237 wrappingNode.appendChild(range.extractContents());
20238 range.insertNode(wrappingNode);
20245 this.execCmd("formatblock", tg);
20249 insertText : function(txt)
20253 var range = this.createRange();
20254 range.deleteContents();
20255 //alert(Sender.getAttribute('label'));
20257 range.insertNode(this.doc.createTextNode(txt));
20263 * Executes a Midas editor command on the editor document and performs necessary focus and
20264 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20265 * @param {String} cmd The Midas command
20266 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20268 relayCmd : function(cmd, value){
20270 this.execCmd(cmd, value);
20271 this.owner.fireEvent('editorevent', this);
20272 //this.updateToolbar();
20273 this.owner.deferFocus();
20277 * Executes a Midas editor command directly on the editor document.
20278 * For visual commands, you should use {@link #relayCmd} instead.
20279 * <b>This should only be called after the editor is initialized.</b>
20280 * @param {String} cmd The Midas command
20281 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20283 execCmd : function(cmd, value){
20284 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20291 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20293 * @param {String} text | dom node..
20295 insertAtCursor : function(text)
20300 if(!this.activated){
20306 var r = this.doc.selection.createRange();
20317 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20321 // from jquery ui (MIT licenced)
20323 var win = this.win;
20325 if (win.getSelection && win.getSelection().getRangeAt) {
20326 range = win.getSelection().getRangeAt(0);
20327 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20328 range.insertNode(node);
20329 } else if (win.document.selection && win.document.selection.createRange) {
20330 // no firefox support
20331 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20332 win.document.selection.createRange().pasteHTML(txt);
20334 // no firefox support
20335 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20336 this.execCmd('InsertHTML', txt);
20345 mozKeyPress : function(e){
20347 var c = e.getCharCode(), cmd;
20350 c = String.fromCharCode(c).toLowerCase();
20364 this.cleanUpPaste.defer(100, this);
20372 e.preventDefault();
20380 fixKeys : function(){ // load time branching for fastest keydown performance
20382 return function(e){
20383 var k = e.getKey(), r;
20386 r = this.doc.selection.createRange();
20389 r.pasteHTML('    ');
20396 r = this.doc.selection.createRange();
20398 var target = r.parentElement();
20399 if(!target || target.tagName.toLowerCase() != 'li'){
20401 r.pasteHTML('<br />');
20407 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20408 this.cleanUpPaste.defer(100, this);
20414 }else if(Roo.isOpera){
20415 return function(e){
20416 var k = e.getKey();
20420 this.execCmd('InsertHTML','    ');
20423 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20424 this.cleanUpPaste.defer(100, this);
20429 }else if(Roo.isSafari){
20430 return function(e){
20431 var k = e.getKey();
20435 this.execCmd('InsertText','\t');
20439 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20440 this.cleanUpPaste.defer(100, this);
20448 getAllAncestors: function()
20450 var p = this.getSelectedNode();
20453 a.push(p); // push blank onto stack..
20454 p = this.getParentElement();
20458 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20462 a.push(this.doc.body);
20466 lastSelNode : false,
20469 getSelection : function()
20471 this.assignDocWin();
20472 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20475 getSelectedNode: function()
20477 // this may only work on Gecko!!!
20479 // should we cache this!!!!
20484 var range = this.createRange(this.getSelection()).cloneRange();
20487 var parent = range.parentElement();
20489 var testRange = range.duplicate();
20490 testRange.moveToElementText(parent);
20491 if (testRange.inRange(range)) {
20494 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20497 parent = parent.parentElement;
20502 // is ancestor a text element.
20503 var ac = range.commonAncestorContainer;
20504 if (ac.nodeType == 3) {
20505 ac = ac.parentNode;
20508 var ar = ac.childNodes;
20511 var other_nodes = [];
20512 var has_other_nodes = false;
20513 for (var i=0;i<ar.length;i++) {
20514 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20517 // fullly contained node.
20519 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20524 // probably selected..
20525 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20526 other_nodes.push(ar[i]);
20530 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20535 has_other_nodes = true;
20537 if (!nodes.length && other_nodes.length) {
20538 nodes= other_nodes;
20540 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20546 createRange: function(sel)
20548 // this has strange effects when using with
20549 // top toolbar - not sure if it's a great idea.
20550 //this.editor.contentWindow.focus();
20551 if (typeof sel != "undefined") {
20553 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20555 return this.doc.createRange();
20558 return this.doc.createRange();
20561 getParentElement: function()
20564 this.assignDocWin();
20565 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20567 var range = this.createRange(sel);
20570 var p = range.commonAncestorContainer;
20571 while (p.nodeType == 3) { // text node
20582 * Range intersection.. the hard stuff...
20586 * [ -- selected range --- ]
20590 * if end is before start or hits it. fail.
20591 * if start is after end or hits it fail.
20593 * if either hits (but other is outside. - then it's not
20599 // @see http://www.thismuchiknow.co.uk/?p=64.
20600 rangeIntersectsNode : function(range, node)
20602 var nodeRange = node.ownerDocument.createRange();
20604 nodeRange.selectNode(node);
20606 nodeRange.selectNodeContents(node);
20609 var rangeStartRange = range.cloneRange();
20610 rangeStartRange.collapse(true);
20612 var rangeEndRange = range.cloneRange();
20613 rangeEndRange.collapse(false);
20615 var nodeStartRange = nodeRange.cloneRange();
20616 nodeStartRange.collapse(true);
20618 var nodeEndRange = nodeRange.cloneRange();
20619 nodeEndRange.collapse(false);
20621 return rangeStartRange.compareBoundaryPoints(
20622 Range.START_TO_START, nodeEndRange) == -1 &&
20623 rangeEndRange.compareBoundaryPoints(
20624 Range.START_TO_START, nodeStartRange) == 1;
20628 rangeCompareNode : function(range, node)
20630 var nodeRange = node.ownerDocument.createRange();
20632 nodeRange.selectNode(node);
20634 nodeRange.selectNodeContents(node);
20638 range.collapse(true);
20640 nodeRange.collapse(true);
20642 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20643 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20645 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20647 var nodeIsBefore = ss == 1;
20648 var nodeIsAfter = ee == -1;
20650 if (nodeIsBefore && nodeIsAfter) {
20653 if (!nodeIsBefore && nodeIsAfter) {
20654 return 1; //right trailed.
20657 if (nodeIsBefore && !nodeIsAfter) {
20658 return 2; // left trailed.
20664 // private? - in a new class?
20665 cleanUpPaste : function()
20667 // cleans up the whole document..
20668 Roo.log('cleanuppaste');
20670 this.cleanUpChildren(this.doc.body);
20671 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20672 if (clean != this.doc.body.innerHTML) {
20673 this.doc.body.innerHTML = clean;
20678 cleanWordChars : function(input) {// change the chars to hex code
20679 var he = Roo.HtmlEditorCore;
20681 var output = input;
20682 Roo.each(he.swapCodes, function(sw) {
20683 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20685 output = output.replace(swapper, sw[1]);
20692 cleanUpChildren : function (n)
20694 if (!n.childNodes.length) {
20697 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20698 this.cleanUpChild(n.childNodes[i]);
20705 cleanUpChild : function (node)
20708 //console.log(node);
20709 if (node.nodeName == "#text") {
20710 // clean up silly Windows -- stuff?
20713 if (node.nodeName == "#comment") {
20714 node.parentNode.removeChild(node);
20715 // clean up silly Windows -- stuff?
20718 var lcname = node.tagName.toLowerCase();
20719 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20720 // whitelist of tags..
20722 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20724 node.parentNode.removeChild(node);
20729 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20731 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20732 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20734 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20735 // remove_keep_children = true;
20738 if (remove_keep_children) {
20739 this.cleanUpChildren(node);
20740 // inserts everything just before this node...
20741 while (node.childNodes.length) {
20742 var cn = node.childNodes[0];
20743 node.removeChild(cn);
20744 node.parentNode.insertBefore(cn, node);
20746 node.parentNode.removeChild(node);
20750 if (!node.attributes || !node.attributes.length) {
20751 this.cleanUpChildren(node);
20755 function cleanAttr(n,v)
20758 if (v.match(/^\./) || v.match(/^\//)) {
20761 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20764 if (v.match(/^#/)) {
20767 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20768 node.removeAttribute(n);
20772 var cwhite = this.cwhite;
20773 var cblack = this.cblack;
20775 function cleanStyle(n,v)
20777 if (v.match(/expression/)) { //XSS?? should we even bother..
20778 node.removeAttribute(n);
20782 var parts = v.split(/;/);
20785 Roo.each(parts, function(p) {
20786 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20790 var l = p.split(':').shift().replace(/\s+/g,'');
20791 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20793 if ( cwhite.length && cblack.indexOf(l) > -1) {
20794 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20795 //node.removeAttribute(n);
20799 // only allow 'c whitelisted system attributes'
20800 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20801 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20802 //node.removeAttribute(n);
20812 if (clean.length) {
20813 node.setAttribute(n, clean.join(';'));
20815 node.removeAttribute(n);
20821 for (var i = node.attributes.length-1; i > -1 ; i--) {
20822 var a = node.attributes[i];
20825 if (a.name.toLowerCase().substr(0,2)=='on') {
20826 node.removeAttribute(a.name);
20829 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20830 node.removeAttribute(a.name);
20833 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20834 cleanAttr(a.name,a.value); // fixme..
20837 if (a.name == 'style') {
20838 cleanStyle(a.name,a.value);
20841 /// clean up MS crap..
20842 // tecnically this should be a list of valid class'es..
20845 if (a.name == 'class') {
20846 if (a.value.match(/^Mso/)) {
20847 node.className = '';
20850 if (a.value.match(/body/)) {
20851 node.className = '';
20862 this.cleanUpChildren(node);
20868 * Clean up MS wordisms...
20870 cleanWord : function(node)
20875 this.cleanWord(this.doc.body);
20878 if (node.nodeName == "#text") {
20879 // clean up silly Windows -- stuff?
20882 if (node.nodeName == "#comment") {
20883 node.parentNode.removeChild(node);
20884 // clean up silly Windows -- stuff?
20888 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20889 node.parentNode.removeChild(node);
20893 // remove - but keep children..
20894 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20895 while (node.childNodes.length) {
20896 var cn = node.childNodes[0];
20897 node.removeChild(cn);
20898 node.parentNode.insertBefore(cn, node);
20900 node.parentNode.removeChild(node);
20901 this.iterateChildren(node, this.cleanWord);
20905 if (node.className.length) {
20907 var cn = node.className.split(/\W+/);
20909 Roo.each(cn, function(cls) {
20910 if (cls.match(/Mso[a-zA-Z]+/)) {
20915 node.className = cna.length ? cna.join(' ') : '';
20917 node.removeAttribute("class");
20921 if (node.hasAttribute("lang")) {
20922 node.removeAttribute("lang");
20925 if (node.hasAttribute("style")) {
20927 var styles = node.getAttribute("style").split(";");
20929 Roo.each(styles, function(s) {
20930 if (!s.match(/:/)) {
20933 var kv = s.split(":");
20934 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20937 // what ever is left... we allow.
20940 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20941 if (!nstyle.length) {
20942 node.removeAttribute('style');
20945 this.iterateChildren(node, this.cleanWord);
20951 * iterateChildren of a Node, calling fn each time, using this as the scole..
20952 * @param {DomNode} node node to iterate children of.
20953 * @param {Function} fn method of this class to call on each item.
20955 iterateChildren : function(node, fn)
20957 if (!node.childNodes.length) {
20960 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20961 fn.call(this, node.childNodes[i])
20967 * cleanTableWidths.
20969 * Quite often pasting from word etc.. results in tables with column and widths.
20970 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20973 cleanTableWidths : function(node)
20978 this.cleanTableWidths(this.doc.body);
20983 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20986 Roo.log(node.tagName);
20987 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20988 this.iterateChildren(node, this.cleanTableWidths);
20991 if (node.hasAttribute('width')) {
20992 node.removeAttribute('width');
20996 if (node.hasAttribute("style")) {
20999 var styles = node.getAttribute("style").split(";");
21001 Roo.each(styles, function(s) {
21002 if (!s.match(/:/)) {
21005 var kv = s.split(":");
21006 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21009 // what ever is left... we allow.
21012 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21013 if (!nstyle.length) {
21014 node.removeAttribute('style');
21018 this.iterateChildren(node, this.cleanTableWidths);
21026 domToHTML : function(currentElement, depth, nopadtext) {
21028 depth = depth || 0;
21029 nopadtext = nopadtext || false;
21031 if (!currentElement) {
21032 return this.domToHTML(this.doc.body);
21035 //Roo.log(currentElement);
21037 var allText = false;
21038 var nodeName = currentElement.nodeName;
21039 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21041 if (nodeName == '#text') {
21043 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21048 if (nodeName != 'BODY') {
21051 // Prints the node tagName, such as <A>, <IMG>, etc
21054 for(i = 0; i < currentElement.attributes.length;i++) {
21056 var aname = currentElement.attributes.item(i).name;
21057 if (!currentElement.attributes.item(i).value.length) {
21060 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21063 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21072 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21075 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21080 // Traverse the tree
21082 var currentElementChild = currentElement.childNodes.item(i);
21083 var allText = true;
21084 var innerHTML = '';
21086 while (currentElementChild) {
21087 // Formatting code (indent the tree so it looks nice on the screen)
21088 var nopad = nopadtext;
21089 if (lastnode == 'SPAN') {
21093 if (currentElementChild.nodeName == '#text') {
21094 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21095 toadd = nopadtext ? toadd : toadd.trim();
21096 if (!nopad && toadd.length > 80) {
21097 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21099 innerHTML += toadd;
21102 currentElementChild = currentElement.childNodes.item(i);
21108 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21110 // Recursively traverse the tree structure of the child node
21111 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21112 lastnode = currentElementChild.nodeName;
21114 currentElementChild=currentElement.childNodes.item(i);
21120 // The remaining code is mostly for formatting the tree
21121 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21126 ret+= "</"+tagName+">";
21132 applyBlacklists : function()
21134 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21135 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21139 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21140 if (b.indexOf(tag) > -1) {
21143 this.white.push(tag);
21147 Roo.each(w, function(tag) {
21148 if (b.indexOf(tag) > -1) {
21151 if (this.white.indexOf(tag) > -1) {
21154 this.white.push(tag);
21159 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21160 if (w.indexOf(tag) > -1) {
21163 this.black.push(tag);
21167 Roo.each(b, function(tag) {
21168 if (w.indexOf(tag) > -1) {
21171 if (this.black.indexOf(tag) > -1) {
21174 this.black.push(tag);
21179 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21180 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21184 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21185 if (b.indexOf(tag) > -1) {
21188 this.cwhite.push(tag);
21192 Roo.each(w, function(tag) {
21193 if (b.indexOf(tag) > -1) {
21196 if (this.cwhite.indexOf(tag) > -1) {
21199 this.cwhite.push(tag);
21204 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21205 if (w.indexOf(tag) > -1) {
21208 this.cblack.push(tag);
21212 Roo.each(b, function(tag) {
21213 if (w.indexOf(tag) > -1) {
21216 if (this.cblack.indexOf(tag) > -1) {
21219 this.cblack.push(tag);
21224 setStylesheets : function(stylesheets)
21226 if(typeof(stylesheets) == 'string'){
21227 Roo.get(this.iframe.contentDocument.head).createChild({
21229 rel : 'stylesheet',
21238 Roo.each(stylesheets, function(s) {
21243 Roo.get(_this.iframe.contentDocument.head).createChild({
21245 rel : 'stylesheet',
21254 removeStylesheets : function()
21258 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21263 // hide stuff that is not compatible
21277 * @event specialkey
21281 * @cfg {String} fieldClass @hide
21284 * @cfg {String} focusClass @hide
21287 * @cfg {String} autoCreate @hide
21290 * @cfg {String} inputType @hide
21293 * @cfg {String} invalidClass @hide
21296 * @cfg {String} invalidText @hide
21299 * @cfg {String} msgFx @hide
21302 * @cfg {String} validateOnBlur @hide
21306 Roo.HtmlEditorCore.white = [
21307 'area', 'br', 'img', 'input', 'hr', 'wbr',
21309 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21310 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21311 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21312 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21313 'table', 'ul', 'xmp',
21315 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21318 'dir', 'menu', 'ol', 'ul', 'dl',
21324 Roo.HtmlEditorCore.black = [
21325 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21327 'base', 'basefont', 'bgsound', 'blink', 'body',
21328 'frame', 'frameset', 'head', 'html', 'ilayer',
21329 'iframe', 'layer', 'link', 'meta', 'object',
21330 'script', 'style' ,'title', 'xml' // clean later..
21332 Roo.HtmlEditorCore.clean = [
21333 'script', 'style', 'title', 'xml'
21335 Roo.HtmlEditorCore.remove = [
21340 Roo.HtmlEditorCore.ablack = [
21344 Roo.HtmlEditorCore.aclean = [
21345 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21349 Roo.HtmlEditorCore.pwhite= [
21350 'http', 'https', 'mailto'
21353 // white listed style attributes.
21354 Roo.HtmlEditorCore.cwhite= [
21355 // 'text-align', /// default is to allow most things..
21361 // black listed style attributes.
21362 Roo.HtmlEditorCore.cblack= [
21363 // 'font-size' -- this can be set by the project
21367 Roo.HtmlEditorCore.swapCodes =[
21386 * @class Roo.bootstrap.HtmlEditor
21387 * @extends Roo.bootstrap.TextArea
21388 * Bootstrap HtmlEditor class
21391 * Create a new HtmlEditor
21392 * @param {Object} config The config object
21395 Roo.bootstrap.HtmlEditor = function(config){
21396 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21397 if (!this.toolbars) {
21398 this.toolbars = [];
21400 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21403 * @event initialize
21404 * Fires when the editor is fully initialized (including the iframe)
21405 * @param {HtmlEditor} this
21410 * Fires when the editor is first receives the focus. Any insertion must wait
21411 * until after this event.
21412 * @param {HtmlEditor} this
21416 * @event beforesync
21417 * Fires before the textarea is updated with content from the editor iframe. Return false
21418 * to cancel the sync.
21419 * @param {HtmlEditor} this
21420 * @param {String} html
21424 * @event beforepush
21425 * Fires before the iframe editor is updated with content from the textarea. Return false
21426 * to cancel the push.
21427 * @param {HtmlEditor} this
21428 * @param {String} html
21433 * Fires when the textarea is updated with content from the editor iframe.
21434 * @param {HtmlEditor} this
21435 * @param {String} html
21440 * Fires when the iframe editor is updated with content from the textarea.
21441 * @param {HtmlEditor} this
21442 * @param {String} html
21446 * @event editmodechange
21447 * Fires when the editor switches edit modes
21448 * @param {HtmlEditor} this
21449 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21451 editmodechange: true,
21453 * @event editorevent
21454 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21455 * @param {HtmlEditor} this
21459 * @event firstfocus
21460 * Fires when on first focus - needed by toolbars..
21461 * @param {HtmlEditor} this
21466 * Auto save the htmlEditor value as a file into Events
21467 * @param {HtmlEditor} this
21471 * @event savedpreview
21472 * preview the saved version of htmlEditor
21473 * @param {HtmlEditor} this
21480 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21484 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21489 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21494 * @cfg {Number} height (in pixels)
21498 * @cfg {Number} width (in pixels)
21503 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21506 stylesheets: false,
21511 // private properties
21512 validationEvent : false,
21514 initialized : false,
21517 onFocus : Roo.emptyFn,
21519 hideMode:'offsets',
21522 tbContainer : false,
21524 toolbarContainer :function() {
21525 return this.wrap.select('.x-html-editor-tb',true).first();
21529 * Protected method that will not generally be called directly. It
21530 * is called when the editor creates its toolbar. Override this method if you need to
21531 * add custom toolbar buttons.
21532 * @param {HtmlEditor} editor
21534 createToolbar : function(){
21536 Roo.log("create toolbars");
21538 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21539 this.toolbars[0].render(this.toolbarContainer());
21543 // if (!editor.toolbars || !editor.toolbars.length) {
21544 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21547 // for (var i =0 ; i < editor.toolbars.length;i++) {
21548 // editor.toolbars[i] = Roo.factory(
21549 // typeof(editor.toolbars[i]) == 'string' ?
21550 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21551 // Roo.bootstrap.HtmlEditor);
21552 // editor.toolbars[i].init(editor);
21558 onRender : function(ct, position)
21560 // Roo.log("Call onRender: " + this.xtype);
21562 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21564 this.wrap = this.inputEl().wrap({
21565 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21568 this.editorcore.onRender(ct, position);
21570 if (this.resizable) {
21571 this.resizeEl = new Roo.Resizable(this.wrap, {
21575 minHeight : this.height,
21576 height: this.height,
21577 handles : this.resizable,
21580 resize : function(r, w, h) {
21581 _t.onResize(w,h); // -something
21587 this.createToolbar(this);
21590 if(!this.width && this.resizable){
21591 this.setSize(this.wrap.getSize());
21593 if (this.resizeEl) {
21594 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21595 // should trigger onReize..
21601 onResize : function(w, h)
21603 Roo.log('resize: ' +w + ',' + h );
21604 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21608 if(this.inputEl() ){
21609 if(typeof w == 'number'){
21610 var aw = w - this.wrap.getFrameWidth('lr');
21611 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21614 if(typeof h == 'number'){
21615 var tbh = -11; // fixme it needs to tool bar size!
21616 for (var i =0; i < this.toolbars.length;i++) {
21617 // fixme - ask toolbars for heights?
21618 tbh += this.toolbars[i].el.getHeight();
21619 //if (this.toolbars[i].footer) {
21620 // tbh += this.toolbars[i].footer.el.getHeight();
21628 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21629 ah -= 5; // knock a few pixes off for look..
21630 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21634 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21635 this.editorcore.onResize(ew,eh);
21640 * Toggles the editor between standard and source edit mode.
21641 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21643 toggleSourceEdit : function(sourceEditMode)
21645 this.editorcore.toggleSourceEdit(sourceEditMode);
21647 if(this.editorcore.sourceEditMode){
21648 Roo.log('editor - showing textarea');
21651 // Roo.log(this.syncValue());
21653 this.inputEl().removeClass(['hide', 'x-hidden']);
21654 this.inputEl().dom.removeAttribute('tabIndex');
21655 this.inputEl().focus();
21657 Roo.log('editor - hiding textarea');
21659 // Roo.log(this.pushValue());
21662 this.inputEl().addClass(['hide', 'x-hidden']);
21663 this.inputEl().dom.setAttribute('tabIndex', -1);
21664 //this.deferFocus();
21667 if(this.resizable){
21668 this.setSize(this.wrap.getSize());
21671 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21674 // private (for BoxComponent)
21675 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21677 // private (for BoxComponent)
21678 getResizeEl : function(){
21682 // private (for BoxComponent)
21683 getPositionEl : function(){
21688 initEvents : function(){
21689 this.originalValue = this.getValue();
21693 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21696 // markInvalid : Roo.emptyFn,
21698 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21701 // clearInvalid : Roo.emptyFn,
21703 setValue : function(v){
21704 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21705 this.editorcore.pushValue();
21710 deferFocus : function(){
21711 this.focus.defer(10, this);
21715 focus : function(){
21716 this.editorcore.focus();
21722 onDestroy : function(){
21728 for (var i =0; i < this.toolbars.length;i++) {
21729 // fixme - ask toolbars for heights?
21730 this.toolbars[i].onDestroy();
21733 this.wrap.dom.innerHTML = '';
21734 this.wrap.remove();
21739 onFirstFocus : function(){
21740 //Roo.log("onFirstFocus");
21741 this.editorcore.onFirstFocus();
21742 for (var i =0; i < this.toolbars.length;i++) {
21743 this.toolbars[i].onFirstFocus();
21749 syncValue : function()
21751 this.editorcore.syncValue();
21754 pushValue : function()
21756 this.editorcore.pushValue();
21760 // hide stuff that is not compatible
21774 * @event specialkey
21778 * @cfg {String} fieldClass @hide
21781 * @cfg {String} focusClass @hide
21784 * @cfg {String} autoCreate @hide
21787 * @cfg {String} inputType @hide
21790 * @cfg {String} invalidClass @hide
21793 * @cfg {String} invalidText @hide
21796 * @cfg {String} msgFx @hide
21799 * @cfg {String} validateOnBlur @hide
21808 Roo.namespace('Roo.bootstrap.htmleditor');
21810 * @class Roo.bootstrap.HtmlEditorToolbar1
21815 new Roo.bootstrap.HtmlEditor({
21818 new Roo.bootstrap.HtmlEditorToolbar1({
21819 disable : { fonts: 1 , format: 1, ..., ... , ...],
21825 * @cfg {Object} disable List of elements to disable..
21826 * @cfg {Array} btns List of additional buttons.
21830 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21833 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21836 Roo.apply(this, config);
21838 // default disabled, based on 'good practice'..
21839 this.disable = this.disable || {};
21840 Roo.applyIf(this.disable, {
21843 specialElements : true
21845 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21847 this.editor = config.editor;
21848 this.editorcore = config.editor.editorcore;
21850 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21852 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21853 // dont call parent... till later.
21855 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21860 editorcore : false,
21865 "h1","h2","h3","h4","h5","h6",
21867 "abbr", "acronym", "address", "cite", "samp", "var",
21871 onRender : function(ct, position)
21873 // Roo.log("Call onRender: " + this.xtype);
21875 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21877 this.el.dom.style.marginBottom = '0';
21879 var editorcore = this.editorcore;
21880 var editor= this.editor;
21883 var btn = function(id,cmd , toggle, handler){
21885 var event = toggle ? 'toggle' : 'click';
21890 xns: Roo.bootstrap,
21893 enableToggle:toggle !== false,
21895 pressed : toggle ? false : null,
21898 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21899 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21908 xns: Roo.bootstrap,
21909 glyphicon : 'font',
21913 xns: Roo.bootstrap,
21917 Roo.each(this.formats, function(f) {
21918 style.menu.items.push({
21920 xns: Roo.bootstrap,
21921 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21926 editorcore.insertTag(this.tagname);
21933 children.push(style);
21936 btn('bold',false,true);
21937 btn('italic',false,true);
21938 btn('align-left', 'justifyleft',true);
21939 btn('align-center', 'justifycenter',true);
21940 btn('align-right' , 'justifyright',true);
21941 btn('link', false, false, function(btn) {
21942 //Roo.log("create link?");
21943 var url = prompt(this.createLinkText, this.defaultLinkValue);
21944 if(url && url != 'http:/'+'/'){
21945 this.editorcore.relayCmd('createlink', url);
21948 btn('list','insertunorderedlist',true);
21949 btn('pencil', false,true, function(btn){
21952 this.toggleSourceEdit(btn.pressed);
21958 xns: Roo.bootstrap,
21963 xns: Roo.bootstrap,
21968 cog.menu.items.push({
21970 xns: Roo.bootstrap,
21971 html : Clean styles,
21976 editorcore.insertTag(this.tagname);
21985 this.xtype = 'NavSimplebar';
21987 for(var i=0;i< children.length;i++) {
21989 this.buttons.add(this.addxtypeChild(children[i]));
21993 editor.on('editorevent', this.updateToolbar, this);
21995 onBtnClick : function(id)
21997 this.editorcore.relayCmd(id);
21998 this.editorcore.focus();
22002 * Protected method that will not generally be called directly. It triggers
22003 * a toolbar update by reading the markup state of the current selection in the editor.
22005 updateToolbar: function(){
22007 if(!this.editorcore.activated){
22008 this.editor.onFirstFocus(); // is this neeed?
22012 var btns = this.buttons;
22013 var doc = this.editorcore.doc;
22014 btns.get('bold').setActive(doc.queryCommandState('bold'));
22015 btns.get('italic').setActive(doc.queryCommandState('italic'));
22016 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22018 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22019 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22020 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22022 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22023 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22026 var ans = this.editorcore.getAllAncestors();
22027 if (this.formatCombo) {
22030 var store = this.formatCombo.store;
22031 this.formatCombo.setValue("");
22032 for (var i =0; i < ans.length;i++) {
22033 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22035 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22043 // hides menus... - so this cant be on a menu...
22044 Roo.bootstrap.MenuMgr.hideAll();
22046 Roo.bootstrap.MenuMgr.hideAll();
22047 //this.editorsyncValue();
22049 onFirstFocus: function() {
22050 this.buttons.each(function(item){
22054 toggleSourceEdit : function(sourceEditMode){
22057 if(sourceEditMode){
22058 Roo.log("disabling buttons");
22059 this.buttons.each( function(item){
22060 if(item.cmd != 'pencil'){
22066 Roo.log("enabling buttons");
22067 if(this.editorcore.initialized){
22068 this.buttons.each( function(item){
22074 Roo.log("calling toggole on editor");
22075 // tell the editor that it's been pressed..
22076 this.editor.toggleSourceEdit(sourceEditMode);
22086 * @class Roo.bootstrap.Table.AbstractSelectionModel
22087 * @extends Roo.util.Observable
22088 * Abstract base class for grid SelectionModels. It provides the interface that should be
22089 * implemented by descendant classes. This class should not be directly instantiated.
22092 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22093 this.locked = false;
22094 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22098 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22099 /** @ignore Called by the grid automatically. Do not call directly. */
22100 init : function(grid){
22106 * Locks the selections.
22109 this.locked = true;
22113 * Unlocks the selections.
22115 unlock : function(){
22116 this.locked = false;
22120 * Returns true if the selections are locked.
22121 * @return {Boolean}
22123 isLocked : function(){
22124 return this.locked;
22128 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22129 * @class Roo.bootstrap.Table.RowSelectionModel
22130 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22131 * It supports multiple selections and keyboard selection/navigation.
22133 * @param {Object} config
22136 Roo.bootstrap.Table.RowSelectionModel = function(config){
22137 Roo.apply(this, config);
22138 this.selections = new Roo.util.MixedCollection(false, function(o){
22143 this.lastActive = false;
22147 * @event selectionchange
22148 * Fires when the selection changes
22149 * @param {SelectionModel} this
22151 "selectionchange" : true,
22153 * @event afterselectionchange
22154 * Fires after the selection changes (eg. by key press or clicking)
22155 * @param {SelectionModel} this
22157 "afterselectionchange" : true,
22159 * @event beforerowselect
22160 * Fires when a row is selected being selected, return false to cancel.
22161 * @param {SelectionModel} this
22162 * @param {Number} rowIndex The selected index
22163 * @param {Boolean} keepExisting False if other selections will be cleared
22165 "beforerowselect" : true,
22168 * Fires when a row is selected.
22169 * @param {SelectionModel} this
22170 * @param {Number} rowIndex The selected index
22171 * @param {Roo.data.Record} r The record
22173 "rowselect" : true,
22175 * @event rowdeselect
22176 * Fires when a row is deselected.
22177 * @param {SelectionModel} this
22178 * @param {Number} rowIndex The selected index
22180 "rowdeselect" : true
22182 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22183 this.locked = false;
22186 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22188 * @cfg {Boolean} singleSelect
22189 * True to allow selection of only one row at a time (defaults to false)
22191 singleSelect : false,
22194 initEvents : function(){
22196 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22197 this.grid.on("mousedown", this.handleMouseDown, this);
22198 }else{ // allow click to work like normal
22199 this.grid.on("rowclick", this.handleDragableRowClick, this);
22202 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22203 "up" : function(e){
22205 this.selectPrevious(e.shiftKey);
22206 }else if(this.last !== false && this.lastActive !== false){
22207 var last = this.last;
22208 this.selectRange(this.last, this.lastActive-1);
22209 this.grid.getView().focusRow(this.lastActive);
22210 if(last !== false){
22214 this.selectFirstRow();
22216 this.fireEvent("afterselectionchange", this);
22218 "down" : function(e){
22220 this.selectNext(e.shiftKey);
22221 }else if(this.last !== false && this.lastActive !== false){
22222 var last = this.last;
22223 this.selectRange(this.last, this.lastActive+1);
22224 this.grid.getView().focusRow(this.lastActive);
22225 if(last !== false){
22229 this.selectFirstRow();
22231 this.fireEvent("afterselectionchange", this);
22236 var view = this.grid.view;
22237 view.on("refresh", this.onRefresh, this);
22238 view.on("rowupdated", this.onRowUpdated, this);
22239 view.on("rowremoved", this.onRemove, this);
22243 onRefresh : function(){
22244 var ds = this.grid.dataSource, i, v = this.grid.view;
22245 var s = this.selections;
22246 s.each(function(r){
22247 if((i = ds.indexOfId(r.id)) != -1){
22256 onRemove : function(v, index, r){
22257 this.selections.remove(r);
22261 onRowUpdated : function(v, index, r){
22262 if(this.isSelected(r)){
22263 v.onRowSelect(index);
22269 * @param {Array} records The records to select
22270 * @param {Boolean} keepExisting (optional) True to keep existing selections
22272 selectRecords : function(records, keepExisting){
22274 this.clearSelections();
22276 var ds = this.grid.dataSource;
22277 for(var i = 0, len = records.length; i < len; i++){
22278 this.selectRow(ds.indexOf(records[i]), true);
22283 * Gets the number of selected rows.
22286 getCount : function(){
22287 return this.selections.length;
22291 * Selects the first row in the grid.
22293 selectFirstRow : function(){
22298 * Select the last row.
22299 * @param {Boolean} keepExisting (optional) True to keep existing selections
22301 selectLastRow : function(keepExisting){
22302 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22306 * Selects the row immediately following the last selected row.
22307 * @param {Boolean} keepExisting (optional) True to keep existing selections
22309 selectNext : function(keepExisting){
22310 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22311 this.selectRow(this.last+1, keepExisting);
22312 this.grid.getView().focusRow(this.last);
22317 * Selects the row that precedes the last selected row.
22318 * @param {Boolean} keepExisting (optional) True to keep existing selections
22320 selectPrevious : function(keepExisting){
22322 this.selectRow(this.last-1, keepExisting);
22323 this.grid.getView().focusRow(this.last);
22328 * Returns the selected records
22329 * @return {Array} Array of selected records
22331 getSelections : function(){
22332 return [].concat(this.selections.items);
22336 * Returns the first selected record.
22339 getSelected : function(){
22340 return this.selections.itemAt(0);
22345 * Clears all selections.
22347 clearSelections : function(fast){
22352 var ds = this.grid.dataSource;
22353 var s = this.selections;
22354 s.each(function(r){
22355 this.deselectRow(ds.indexOfId(r.id));
22359 this.selections.clear();
22366 * Selects all rows.
22368 selectAll : function(){
22372 this.selections.clear();
22373 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22374 this.selectRow(i, true);
22379 * Returns True if there is a selection.
22380 * @return {Boolean}
22382 hasSelection : function(){
22383 return this.selections.length > 0;
22387 * Returns True if the specified row is selected.
22388 * @param {Number/Record} record The record or index of the record to check
22389 * @return {Boolean}
22391 isSelected : function(index){
22392 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22393 return (r && this.selections.key(r.id) ? true : false);
22397 * Returns True if the specified record id is selected.
22398 * @param {String} id The id of record to check
22399 * @return {Boolean}
22401 isIdSelected : function(id){
22402 return (this.selections.key(id) ? true : false);
22406 handleMouseDown : function(e, t){
22407 var view = this.grid.getView(), rowIndex;
22408 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22411 if(e.shiftKey && this.last !== false){
22412 var last = this.last;
22413 this.selectRange(last, rowIndex, e.ctrlKey);
22414 this.last = last; // reset the last
22415 view.focusRow(rowIndex);
22417 var isSelected = this.isSelected(rowIndex);
22418 if(e.button !== 0 && isSelected){
22419 view.focusRow(rowIndex);
22420 }else if(e.ctrlKey && isSelected){
22421 this.deselectRow(rowIndex);
22422 }else if(!isSelected){
22423 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22424 view.focusRow(rowIndex);
22427 this.fireEvent("afterselectionchange", this);
22430 handleDragableRowClick : function(grid, rowIndex, e)
22432 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22433 this.selectRow(rowIndex, false);
22434 grid.view.focusRow(rowIndex);
22435 this.fireEvent("afterselectionchange", this);
22440 * Selects multiple rows.
22441 * @param {Array} rows Array of the indexes of the row to select
22442 * @param {Boolean} keepExisting (optional) True to keep existing selections
22444 selectRows : function(rows, keepExisting){
22446 this.clearSelections();
22448 for(var i = 0, len = rows.length; i < len; i++){
22449 this.selectRow(rows[i], true);
22454 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22455 * @param {Number} startRow The index of the first row in the range
22456 * @param {Number} endRow The index of the last row in the range
22457 * @param {Boolean} keepExisting (optional) True to retain existing selections
22459 selectRange : function(startRow, endRow, keepExisting){
22464 this.clearSelections();
22466 if(startRow <= endRow){
22467 for(var i = startRow; i <= endRow; i++){
22468 this.selectRow(i, true);
22471 for(var i = startRow; i >= endRow; i--){
22472 this.selectRow(i, true);
22478 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22479 * @param {Number} startRow The index of the first row in the range
22480 * @param {Number} endRow The index of the last row in the range
22482 deselectRange : function(startRow, endRow, preventViewNotify){
22486 for(var i = startRow; i <= endRow; i++){
22487 this.deselectRow(i, preventViewNotify);
22493 * @param {Number} row The index of the row to select
22494 * @param {Boolean} keepExisting (optional) True to keep existing selections
22496 selectRow : function(index, keepExisting, preventViewNotify){
22497 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22500 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22501 if(!keepExisting || this.singleSelect){
22502 this.clearSelections();
22504 var r = this.grid.dataSource.getAt(index);
22505 this.selections.add(r);
22506 this.last = this.lastActive = index;
22507 if(!preventViewNotify){
22508 this.grid.getView().onRowSelect(index);
22510 this.fireEvent("rowselect", this, index, r);
22511 this.fireEvent("selectionchange", this);
22517 * @param {Number} row The index of the row to deselect
22519 deselectRow : function(index, preventViewNotify){
22523 if(this.last == index){
22526 if(this.lastActive == index){
22527 this.lastActive = false;
22529 var r = this.grid.dataSource.getAt(index);
22530 this.selections.remove(r);
22531 if(!preventViewNotify){
22532 this.grid.getView().onRowDeselect(index);
22534 this.fireEvent("rowdeselect", this, index);
22535 this.fireEvent("selectionchange", this);
22539 restoreLast : function(){
22541 this.last = this._last;
22546 acceptsNav : function(row, col, cm){
22547 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22551 onEditorKey : function(field, e){
22552 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22557 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22559 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22561 }else if(k == e.ENTER && !e.ctrlKey){
22565 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22567 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22569 }else if(k == e.ESC){
22573 g.startEditing(newCell[0], newCell[1]);
22578 * Ext JS Library 1.1.1
22579 * Copyright(c) 2006-2007, Ext JS, LLC.
22581 * Originally Released Under LGPL - original licence link has changed is not relivant.
22584 * <script type="text/javascript">
22588 * @class Roo.bootstrap.PagingToolbar
22589 * @extends Roo.bootstrap.NavSimplebar
22590 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22592 * Create a new PagingToolbar
22593 * @param {Object} config The config object
22594 * @param {Roo.data.Store} store
22596 Roo.bootstrap.PagingToolbar = function(config)
22598 // old args format still supported... - xtype is prefered..
22599 // created from xtype...
22601 this.ds = config.dataSource;
22603 if (config.store && !this.ds) {
22604 this.store= Roo.factory(config.store, Roo.data);
22605 this.ds = this.store;
22606 this.ds.xmodule = this.xmodule || false;
22609 this.toolbarItems = [];
22610 if (config.items) {
22611 this.toolbarItems = config.items;
22614 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22619 this.bind(this.ds);
22622 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22626 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22628 * @cfg {Roo.data.Store} dataSource
22629 * The underlying data store providing the paged data
22632 * @cfg {String/HTMLElement/Element} container
22633 * container The id or element that will contain the toolbar
22636 * @cfg {Boolean} displayInfo
22637 * True to display the displayMsg (defaults to false)
22640 * @cfg {Number} pageSize
22641 * The number of records to display per page (defaults to 20)
22645 * @cfg {String} displayMsg
22646 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22648 displayMsg : 'Displaying {0} - {1} of {2}',
22650 * @cfg {String} emptyMsg
22651 * The message to display when no records are found (defaults to "No data to display")
22653 emptyMsg : 'No data to display',
22655 * Customizable piece of the default paging text (defaults to "Page")
22658 beforePageText : "Page",
22660 * Customizable piece of the default paging text (defaults to "of %0")
22663 afterPageText : "of {0}",
22665 * Customizable piece of the default paging text (defaults to "First Page")
22668 firstText : "First Page",
22670 * Customizable piece of the default paging text (defaults to "Previous Page")
22673 prevText : "Previous Page",
22675 * Customizable piece of the default paging text (defaults to "Next Page")
22678 nextText : "Next Page",
22680 * Customizable piece of the default paging text (defaults to "Last Page")
22683 lastText : "Last Page",
22685 * Customizable piece of the default paging text (defaults to "Refresh")
22688 refreshText : "Refresh",
22692 onRender : function(ct, position)
22694 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22695 this.navgroup.parentId = this.id;
22696 this.navgroup.onRender(this.el, null);
22697 // add the buttons to the navgroup
22699 if(this.displayInfo){
22700 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22701 this.displayEl = this.el.select('.x-paging-info', true).first();
22702 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22703 // this.displayEl = navel.el.select('span',true).first();
22709 Roo.each(_this.buttons, function(e){ // this might need to use render????
22710 Roo.factory(e).onRender(_this.el, null);
22714 Roo.each(_this.toolbarItems, function(e) {
22715 _this.navgroup.addItem(e);
22719 this.first = this.navgroup.addItem({
22720 tooltip: this.firstText,
22722 icon : 'fa fa-backward',
22724 preventDefault: true,
22725 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22728 this.prev = this.navgroup.addItem({
22729 tooltip: this.prevText,
22731 icon : 'fa fa-step-backward',
22733 preventDefault: true,
22734 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22736 //this.addSeparator();
22739 var field = this.navgroup.addItem( {
22741 cls : 'x-paging-position',
22743 html : this.beforePageText +
22744 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22745 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22748 this.field = field.el.select('input', true).first();
22749 this.field.on("keydown", this.onPagingKeydown, this);
22750 this.field.on("focus", function(){this.dom.select();});
22753 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22754 //this.field.setHeight(18);
22755 //this.addSeparator();
22756 this.next = this.navgroup.addItem({
22757 tooltip: this.nextText,
22759 html : ' <i class="fa fa-step-forward">',
22761 preventDefault: true,
22762 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22764 this.last = this.navgroup.addItem({
22765 tooltip: this.lastText,
22766 icon : 'fa fa-forward',
22769 preventDefault: true,
22770 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22772 //this.addSeparator();
22773 this.loading = this.navgroup.addItem({
22774 tooltip: this.refreshText,
22775 icon: 'fa fa-refresh',
22776 preventDefault: true,
22777 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22783 updateInfo : function(){
22784 if(this.displayEl){
22785 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22786 var msg = count == 0 ?
22790 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22792 this.displayEl.update(msg);
22797 onLoad : function(ds, r, o){
22798 this.cursor = o.params ? o.params.start : 0;
22799 var d = this.getPageData(),
22803 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22804 this.field.dom.value = ap;
22805 this.first.setDisabled(ap == 1);
22806 this.prev.setDisabled(ap == 1);
22807 this.next.setDisabled(ap == ps);
22808 this.last.setDisabled(ap == ps);
22809 this.loading.enable();
22814 getPageData : function(){
22815 var total = this.ds.getTotalCount();
22818 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22819 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22824 onLoadError : function(){
22825 this.loading.enable();
22829 onPagingKeydown : function(e){
22830 var k = e.getKey();
22831 var d = this.getPageData();
22833 var v = this.field.dom.value, pageNum;
22834 if(!v || isNaN(pageNum = parseInt(v, 10))){
22835 this.field.dom.value = d.activePage;
22838 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22839 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22842 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))
22844 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22845 this.field.dom.value = pageNum;
22846 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22849 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22851 var v = this.field.dom.value, pageNum;
22852 var increment = (e.shiftKey) ? 10 : 1;
22853 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22856 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22857 this.field.dom.value = d.activePage;
22860 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22862 this.field.dom.value = parseInt(v, 10) + increment;
22863 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22864 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22871 beforeLoad : function(){
22873 this.loading.disable();
22878 onClick : function(which){
22887 ds.load({params:{start: 0, limit: this.pageSize}});
22890 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22893 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22896 var total = ds.getTotalCount();
22897 var extra = total % this.pageSize;
22898 var lastStart = extra ? (total - extra) : total-this.pageSize;
22899 ds.load({params:{start: lastStart, limit: this.pageSize}});
22902 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22908 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22909 * @param {Roo.data.Store} store The data store to unbind
22911 unbind : function(ds){
22912 ds.un("beforeload", this.beforeLoad, this);
22913 ds.un("load", this.onLoad, this);
22914 ds.un("loadexception", this.onLoadError, this);
22915 ds.un("remove", this.updateInfo, this);
22916 ds.un("add", this.updateInfo, this);
22917 this.ds = undefined;
22921 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22922 * @param {Roo.data.Store} store The data store to bind
22924 bind : function(ds){
22925 ds.on("beforeload", this.beforeLoad, this);
22926 ds.on("load", this.onLoad, this);
22927 ds.on("loadexception", this.onLoadError, this);
22928 ds.on("remove", this.updateInfo, this);
22929 ds.on("add", this.updateInfo, this);
22940 * @class Roo.bootstrap.MessageBar
22941 * @extends Roo.bootstrap.Component
22942 * Bootstrap MessageBar class
22943 * @cfg {String} html contents of the MessageBar
22944 * @cfg {String} weight (info | success | warning | danger) default info
22945 * @cfg {String} beforeClass insert the bar before the given class
22946 * @cfg {Boolean} closable (true | false) default false
22947 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22950 * Create a new Element
22951 * @param {Object} config The config object
22954 Roo.bootstrap.MessageBar = function(config){
22955 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22958 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22964 beforeClass: 'bootstrap-sticky-wrap',
22966 getAutoCreate : function(){
22970 cls: 'alert alert-dismissable alert-' + this.weight,
22975 html: this.html || ''
22981 cfg.cls += ' alert-messages-fixed';
22995 onRender : function(ct, position)
22997 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23000 var cfg = Roo.apply({}, this.getAutoCreate());
23004 cfg.cls += ' ' + this.cls;
23007 cfg.style = this.style;
23009 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23011 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23014 this.el.select('>button.close').on('click', this.hide, this);
23020 if (!this.rendered) {
23026 this.fireEvent('show', this);
23032 if (!this.rendered) {
23038 this.fireEvent('hide', this);
23041 update : function()
23043 // var e = this.el.dom.firstChild;
23045 // if(this.closable){
23046 // e = e.nextSibling;
23049 // e.data = this.html || '';
23051 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23067 * @class Roo.bootstrap.Graph
23068 * @extends Roo.bootstrap.Component
23069 * Bootstrap Graph class
23073 @cfg {String} graphtype bar | vbar | pie
23074 @cfg {number} g_x coodinator | centre x (pie)
23075 @cfg {number} g_y coodinator | centre y (pie)
23076 @cfg {number} g_r radius (pie)
23077 @cfg {number} g_height height of the chart (respected by all elements in the set)
23078 @cfg {number} g_width width of the chart (respected by all elements in the set)
23079 @cfg {Object} title The title of the chart
23082 -opts (object) options for the chart
23084 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23085 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23087 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.
23088 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23090 o stretch (boolean)
23092 -opts (object) options for the pie
23095 o startAngle (number)
23096 o endAngle (number)
23100 * Create a new Input
23101 * @param {Object} config The config object
23104 Roo.bootstrap.Graph = function(config){
23105 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23111 * The img click event for the img.
23112 * @param {Roo.EventObject} e
23118 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23129 //g_colors: this.colors,
23136 getAutoCreate : function(){
23147 onRender : function(ct,position){
23150 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23152 if (typeof(Raphael) == 'undefined') {
23153 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23157 this.raphael = Raphael(this.el.dom);
23159 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23160 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23161 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23162 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23164 r.text(160, 10, "Single Series Chart").attr(txtattr);
23165 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23166 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23167 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23169 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23170 r.barchart(330, 10, 300, 220, data1);
23171 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23172 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23175 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23176 // r.barchart(30, 30, 560, 250, xdata, {
23177 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23178 // axis : "0 0 1 1",
23179 // axisxlabels : xdata
23180 // //yvalues : cols,
23183 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23185 // this.load(null,xdata,{
23186 // axis : "0 0 1 1",
23187 // axisxlabels : xdata
23192 load : function(graphtype,xdata,opts)
23194 this.raphael.clear();
23196 graphtype = this.graphtype;
23201 var r = this.raphael,
23202 fin = function () {
23203 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23205 fout = function () {
23206 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23208 pfin = function() {
23209 this.sector.stop();
23210 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23213 this.label[0].stop();
23214 this.label[0].attr({ r: 7.5 });
23215 this.label[1].attr({ "font-weight": 800 });
23218 pfout = function() {
23219 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23222 this.label[0].animate({ r: 5 }, 500, "bounce");
23223 this.label[1].attr({ "font-weight": 400 });
23229 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23232 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23235 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23236 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23238 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23245 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23250 setTitle: function(o)
23255 initEvents: function() {
23258 this.el.on('click', this.onClick, this);
23262 onClick : function(e)
23264 Roo.log('img onclick');
23265 this.fireEvent('click', this, e);
23277 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23280 * @class Roo.bootstrap.dash.NumberBox
23281 * @extends Roo.bootstrap.Component
23282 * Bootstrap NumberBox class
23283 * @cfg {String} headline Box headline
23284 * @cfg {String} content Box content
23285 * @cfg {String} icon Box icon
23286 * @cfg {String} footer Footer text
23287 * @cfg {String} fhref Footer href
23290 * Create a new NumberBox
23291 * @param {Object} config The config object
23295 Roo.bootstrap.dash.NumberBox = function(config){
23296 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23300 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23309 getAutoCreate : function(){
23313 cls : 'small-box ',
23321 cls : 'roo-headline',
23322 html : this.headline
23326 cls : 'roo-content',
23327 html : this.content
23341 cls : 'ion ' + this.icon
23350 cls : 'small-box-footer',
23351 href : this.fhref || '#',
23355 cfg.cn.push(footer);
23362 onRender : function(ct,position){
23363 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23370 setHeadline: function (value)
23372 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23375 setFooter: function (value, href)
23377 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23380 this.el.select('a.small-box-footer',true).first().attr('href', href);
23385 setContent: function (value)
23387 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23390 initEvents: function()
23404 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23407 * @class Roo.bootstrap.dash.TabBox
23408 * @extends Roo.bootstrap.Component
23409 * Bootstrap TabBox class
23410 * @cfg {String} title Title of the TabBox
23411 * @cfg {String} icon Icon of the TabBox
23412 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23413 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23416 * Create a new TabBox
23417 * @param {Object} config The config object
23421 Roo.bootstrap.dash.TabBox = function(config){
23422 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23427 * When a pane is added
23428 * @param {Roo.bootstrap.dash.TabPane} pane
23432 * @event activatepane
23433 * When a pane is activated
23434 * @param {Roo.bootstrap.dash.TabPane} pane
23436 "activatepane" : true
23444 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23449 tabScrollable : false,
23451 getChildContainer : function()
23453 return this.el.select('.tab-content', true).first();
23456 getAutoCreate : function(){
23460 cls: 'pull-left header',
23468 cls: 'fa ' + this.icon
23474 cls: 'nav nav-tabs pull-right',
23480 if(this.tabScrollable){
23487 cls: 'nav nav-tabs pull-right',
23498 cls: 'nav-tabs-custom',
23503 cls: 'tab-content no-padding',
23511 initEvents : function()
23513 //Roo.log('add add pane handler');
23514 this.on('addpane', this.onAddPane, this);
23517 * Updates the box title
23518 * @param {String} html to set the title to.
23520 setTitle : function(value)
23522 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23524 onAddPane : function(pane)
23526 this.panes.push(pane);
23527 //Roo.log('addpane');
23529 // tabs are rendere left to right..
23530 if(!this.showtabs){
23534 var ctr = this.el.select('.nav-tabs', true).first();
23537 var existing = ctr.select('.nav-tab',true);
23538 var qty = existing.getCount();;
23541 var tab = ctr.createChild({
23543 cls : 'nav-tab' + (qty ? '' : ' active'),
23551 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23554 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23556 pane.el.addClass('active');
23561 onTabClick : function(ev,un,ob,pane)
23563 //Roo.log('tab - prev default');
23564 ev.preventDefault();
23567 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23568 pane.tab.addClass('active');
23569 //Roo.log(pane.title);
23570 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23571 // technically we should have a deactivate event.. but maybe add later.
23572 // and it should not de-activate the selected tab...
23573 this.fireEvent('activatepane', pane);
23574 pane.el.addClass('active');
23575 pane.fireEvent('activate');
23580 getActivePane : function()
23583 Roo.each(this.panes, function(p) {
23584 if(p.el.hasClass('active')){
23605 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23607 * @class Roo.bootstrap.TabPane
23608 * @extends Roo.bootstrap.Component
23609 * Bootstrap TabPane class
23610 * @cfg {Boolean} active (false | true) Default false
23611 * @cfg {String} title title of panel
23615 * Create a new TabPane
23616 * @param {Object} config The config object
23619 Roo.bootstrap.dash.TabPane = function(config){
23620 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23626 * When a pane is activated
23627 * @param {Roo.bootstrap.dash.TabPane} pane
23634 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23639 // the tabBox that this is attached to.
23642 getAutoCreate : function()
23650 cfg.cls += ' active';
23655 initEvents : function()
23657 //Roo.log('trigger add pane handler');
23658 this.parent().fireEvent('addpane', this)
23662 * Updates the tab title
23663 * @param {String} html to set the title to.
23665 setTitle: function(str)
23671 this.tab.select('a', true).first().dom.innerHTML = str;
23688 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23691 * @class Roo.bootstrap.menu.Menu
23692 * @extends Roo.bootstrap.Component
23693 * Bootstrap Menu class - container for Menu
23694 * @cfg {String} html Text of the menu
23695 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23696 * @cfg {String} icon Font awesome icon
23697 * @cfg {String} pos Menu align to (top | bottom) default bottom
23701 * Create a new Menu
23702 * @param {Object} config The config object
23706 Roo.bootstrap.menu.Menu = function(config){
23707 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23711 * @event beforeshow
23712 * Fires before this menu is displayed
23713 * @param {Roo.bootstrap.menu.Menu} this
23717 * @event beforehide
23718 * Fires before this menu is hidden
23719 * @param {Roo.bootstrap.menu.Menu} this
23724 * Fires after this menu is displayed
23725 * @param {Roo.bootstrap.menu.Menu} this
23730 * Fires after this menu is hidden
23731 * @param {Roo.bootstrap.menu.Menu} this
23736 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23737 * @param {Roo.bootstrap.menu.Menu} this
23738 * @param {Roo.EventObject} e
23745 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23749 weight : 'default',
23754 getChildContainer : function() {
23755 if(this.isSubMenu){
23759 return this.el.select('ul.dropdown-menu', true).first();
23762 getAutoCreate : function()
23767 cls : 'roo-menu-text',
23775 cls : 'fa ' + this.icon
23786 cls : 'dropdown-button btn btn-' + this.weight,
23791 cls : 'dropdown-toggle btn btn-' + this.weight,
23801 cls : 'dropdown-menu'
23807 if(this.pos == 'top'){
23808 cfg.cls += ' dropup';
23811 if(this.isSubMenu){
23814 cls : 'dropdown-menu'
23821 onRender : function(ct, position)
23823 this.isSubMenu = ct.hasClass('dropdown-submenu');
23825 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23828 initEvents : function()
23830 if(this.isSubMenu){
23834 this.hidden = true;
23836 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23837 this.triggerEl.on('click', this.onTriggerPress, this);
23839 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23840 this.buttonEl.on('click', this.onClick, this);
23846 if(this.isSubMenu){
23850 return this.el.select('ul.dropdown-menu', true).first();
23853 onClick : function(e)
23855 this.fireEvent("click", this, e);
23858 onTriggerPress : function(e)
23860 if (this.isVisible()) {
23867 isVisible : function(){
23868 return !this.hidden;
23873 this.fireEvent("beforeshow", this);
23875 this.hidden = false;
23876 this.el.addClass('open');
23878 Roo.get(document).on("mouseup", this.onMouseUp, this);
23880 this.fireEvent("show", this);
23887 this.fireEvent("beforehide", this);
23889 this.hidden = true;
23890 this.el.removeClass('open');
23892 Roo.get(document).un("mouseup", this.onMouseUp);
23894 this.fireEvent("hide", this);
23897 onMouseUp : function()
23911 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23914 * @class Roo.bootstrap.menu.Item
23915 * @extends Roo.bootstrap.Component
23916 * Bootstrap MenuItem class
23917 * @cfg {Boolean} submenu (true | false) default false
23918 * @cfg {String} html text of the item
23919 * @cfg {String} href the link
23920 * @cfg {Boolean} disable (true | false) default false
23921 * @cfg {Boolean} preventDefault (true | false) default true
23922 * @cfg {String} icon Font awesome icon
23923 * @cfg {String} pos Submenu align to (left | right) default right
23927 * Create a new Item
23928 * @param {Object} config The config object
23932 Roo.bootstrap.menu.Item = function(config){
23933 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23937 * Fires when the mouse is hovering over this menu
23938 * @param {Roo.bootstrap.menu.Item} this
23939 * @param {Roo.EventObject} e
23944 * Fires when the mouse exits this menu
23945 * @param {Roo.bootstrap.menu.Item} this
23946 * @param {Roo.EventObject} e
23952 * The raw click event for the entire grid.
23953 * @param {Roo.EventObject} e
23959 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23964 preventDefault: true,
23969 getAutoCreate : function()
23974 cls : 'roo-menu-item-text',
23982 cls : 'fa ' + this.icon
23991 href : this.href || '#',
23998 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24002 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24004 if(this.pos == 'left'){
24005 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24012 initEvents : function()
24014 this.el.on('mouseover', this.onMouseOver, this);
24015 this.el.on('mouseout', this.onMouseOut, this);
24017 this.el.select('a', true).first().on('click', this.onClick, this);
24021 onClick : function(e)
24023 if(this.preventDefault){
24024 e.preventDefault();
24027 this.fireEvent("click", this, e);
24030 onMouseOver : function(e)
24032 if(this.submenu && this.pos == 'left'){
24033 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24036 this.fireEvent("mouseover", this, e);
24039 onMouseOut : function(e)
24041 this.fireEvent("mouseout", this, e);
24053 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24056 * @class Roo.bootstrap.menu.Separator
24057 * @extends Roo.bootstrap.Component
24058 * Bootstrap Separator class
24061 * Create a new Separator
24062 * @param {Object} config The config object
24066 Roo.bootstrap.menu.Separator = function(config){
24067 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24070 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24072 getAutoCreate : function(){
24093 * @class Roo.bootstrap.Tooltip
24094 * Bootstrap Tooltip class
24095 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24096 * to determine which dom element triggers the tooltip.
24098 * It needs to add support for additional attributes like tooltip-position
24101 * Create a new Toolti
24102 * @param {Object} config The config object
24105 Roo.bootstrap.Tooltip = function(config){
24106 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24109 Roo.apply(Roo.bootstrap.Tooltip, {
24111 * @function init initialize tooltip monitoring.
24115 currentTip : false,
24116 currentRegion : false,
24122 Roo.get(document).on('mouseover', this.enter ,this);
24123 Roo.get(document).on('mouseout', this.leave, this);
24126 this.currentTip = new Roo.bootstrap.Tooltip();
24129 enter : function(ev)
24131 var dom = ev.getTarget();
24133 //Roo.log(['enter',dom]);
24134 var el = Roo.fly(dom);
24135 if (this.currentEl) {
24137 //Roo.log(this.currentEl);
24138 //Roo.log(this.currentEl.contains(dom));
24139 if (this.currentEl == el) {
24142 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24148 if (this.currentTip.el) {
24149 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24154 // you can not look for children, as if el is the body.. then everythign is the child..
24155 if (!el.attr('tooltip')) { //
24156 if (!el.select("[tooltip]").elements.length) {
24159 // is the mouse over this child...?
24160 bindEl = el.select("[tooltip]").first();
24161 var xy = ev.getXY();
24162 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24163 //Roo.log("not in region.");
24166 //Roo.log("child element over..");
24169 this.currentEl = bindEl;
24170 this.currentTip.bind(bindEl);
24171 this.currentRegion = Roo.lib.Region.getRegion(dom);
24172 this.currentTip.enter();
24175 leave : function(ev)
24177 var dom = ev.getTarget();
24178 //Roo.log(['leave',dom]);
24179 if (!this.currentEl) {
24184 if (dom != this.currentEl.dom) {
24187 var xy = ev.getXY();
24188 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24191 // only activate leave if mouse cursor is outside... bounding box..
24196 if (this.currentTip) {
24197 this.currentTip.leave();
24199 //Roo.log('clear currentEl');
24200 this.currentEl = false;
24205 'left' : ['r-l', [-2,0], 'right'],
24206 'right' : ['l-r', [2,0], 'left'],
24207 'bottom' : ['t-b', [0,2], 'top'],
24208 'top' : [ 'b-t', [0,-2], 'bottom']
24214 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24219 delay : null, // can be { show : 300 , hide: 500}
24223 hoverState : null, //???
24225 placement : 'bottom',
24227 getAutoCreate : function(){
24234 cls : 'tooltip-arrow'
24237 cls : 'tooltip-inner'
24244 bind : function(el)
24250 enter : function () {
24252 if (this.timeout != null) {
24253 clearTimeout(this.timeout);
24256 this.hoverState = 'in';
24257 //Roo.log("enter - show");
24258 if (!this.delay || !this.delay.show) {
24263 this.timeout = setTimeout(function () {
24264 if (_t.hoverState == 'in') {
24267 }, this.delay.show);
24271 clearTimeout(this.timeout);
24273 this.hoverState = 'out';
24274 if (!this.delay || !this.delay.hide) {
24280 this.timeout = setTimeout(function () {
24281 //Roo.log("leave - timeout");
24283 if (_t.hoverState == 'out') {
24285 Roo.bootstrap.Tooltip.currentEl = false;
24293 this.render(document.body);
24296 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24298 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24300 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24302 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24304 var placement = typeof this.placement == 'function' ?
24305 this.placement.call(this, this.el, on_el) :
24308 var autoToken = /\s?auto?\s?/i;
24309 var autoPlace = autoToken.test(placement);
24311 placement = placement.replace(autoToken, '') || 'top';
24315 //this.el.setXY([0,0]);
24317 //this.el.dom.style.display='block';
24319 //this.el.appendTo(on_el);
24321 var p = this.getPosition();
24322 var box = this.el.getBox();
24328 var align = Roo.bootstrap.Tooltip.alignment[placement];
24330 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24332 if(placement == 'top' || placement == 'bottom'){
24334 placement = 'right';
24337 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24338 placement = 'left';
24342 align = Roo.bootstrap.Tooltip.alignment[placement];
24344 this.el.alignTo(this.bindEl, align[0],align[1]);
24345 //var arrow = this.el.select('.arrow',true).first();
24346 //arrow.set(align[2],
24348 this.el.addClass(placement);
24350 this.el.addClass('in fade');
24352 this.hoverState = null;
24354 if (this.el.hasClass('fade')) {
24365 //this.el.setXY([0,0]);
24366 this.el.removeClass('in');
24382 * @class Roo.bootstrap.LocationPicker
24383 * @extends Roo.bootstrap.Component
24384 * Bootstrap LocationPicker class
24385 * @cfg {Number} latitude Position when init default 0
24386 * @cfg {Number} longitude Position when init default 0
24387 * @cfg {Number} zoom default 15
24388 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24389 * @cfg {Boolean} mapTypeControl default false
24390 * @cfg {Boolean} disableDoubleClickZoom default false
24391 * @cfg {Boolean} scrollwheel default true
24392 * @cfg {Boolean} streetViewControl default false
24393 * @cfg {Number} radius default 0
24394 * @cfg {String} locationName
24395 * @cfg {Boolean} draggable default true
24396 * @cfg {Boolean} enableAutocomplete default false
24397 * @cfg {Boolean} enableReverseGeocode default true
24398 * @cfg {String} markerTitle
24401 * Create a new LocationPicker
24402 * @param {Object} config The config object
24406 Roo.bootstrap.LocationPicker = function(config){
24408 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24413 * Fires when the picker initialized.
24414 * @param {Roo.bootstrap.LocationPicker} this
24415 * @param {Google Location} location
24419 * @event positionchanged
24420 * Fires when the picker position changed.
24421 * @param {Roo.bootstrap.LocationPicker} this
24422 * @param {Google Location} location
24424 positionchanged : true,
24427 * Fires when the map resize.
24428 * @param {Roo.bootstrap.LocationPicker} this
24433 * Fires when the map show.
24434 * @param {Roo.bootstrap.LocationPicker} this
24439 * Fires when the map hide.
24440 * @param {Roo.bootstrap.LocationPicker} this
24445 * Fires when click the map.
24446 * @param {Roo.bootstrap.LocationPicker} this
24447 * @param {Map event} e
24451 * @event mapRightClick
24452 * Fires when right click the map.
24453 * @param {Roo.bootstrap.LocationPicker} this
24454 * @param {Map event} e
24456 mapRightClick : true,
24458 * @event markerClick
24459 * Fires when click the marker.
24460 * @param {Roo.bootstrap.LocationPicker} this
24461 * @param {Map event} e
24463 markerClick : true,
24465 * @event markerRightClick
24466 * Fires when right click the marker.
24467 * @param {Roo.bootstrap.LocationPicker} this
24468 * @param {Map event} e
24470 markerRightClick : true,
24472 * @event OverlayViewDraw
24473 * Fires when OverlayView Draw
24474 * @param {Roo.bootstrap.LocationPicker} this
24476 OverlayViewDraw : true,
24478 * @event OverlayViewOnAdd
24479 * Fires when OverlayView Draw
24480 * @param {Roo.bootstrap.LocationPicker} this
24482 OverlayViewOnAdd : true,
24484 * @event OverlayViewOnRemove
24485 * Fires when OverlayView Draw
24486 * @param {Roo.bootstrap.LocationPicker} this
24488 OverlayViewOnRemove : true,
24490 * @event OverlayViewShow
24491 * Fires when OverlayView Draw
24492 * @param {Roo.bootstrap.LocationPicker} this
24493 * @param {Pixel} cpx
24495 OverlayViewShow : true,
24497 * @event OverlayViewHide
24498 * Fires when OverlayView Draw
24499 * @param {Roo.bootstrap.LocationPicker} this
24501 OverlayViewHide : true,
24503 * @event loadexception
24504 * Fires when load google lib failed.
24505 * @param {Roo.bootstrap.LocationPicker} this
24507 loadexception : true
24512 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24514 gMapContext: false,
24520 mapTypeControl: false,
24521 disableDoubleClickZoom: false,
24523 streetViewControl: false,
24527 enableAutocomplete: false,
24528 enableReverseGeocode: true,
24531 getAutoCreate: function()
24536 cls: 'roo-location-picker'
24542 initEvents: function(ct, position)
24544 if(!this.el.getWidth() || this.isApplied()){
24548 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24553 initial: function()
24555 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24556 this.fireEvent('loadexception', this);
24560 if(!this.mapTypeId){
24561 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24564 this.gMapContext = this.GMapContext();
24566 this.initOverlayView();
24568 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24572 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24573 _this.setPosition(_this.gMapContext.marker.position);
24576 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24577 _this.fireEvent('mapClick', this, event);
24581 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24582 _this.fireEvent('mapRightClick', this, event);
24586 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24587 _this.fireEvent('markerClick', this, event);
24591 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24592 _this.fireEvent('markerRightClick', this, event);
24596 this.setPosition(this.gMapContext.location);
24598 this.fireEvent('initial', this, this.gMapContext.location);
24601 initOverlayView: function()
24605 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24609 _this.fireEvent('OverlayViewDraw', _this);
24614 _this.fireEvent('OverlayViewOnAdd', _this);
24617 onRemove: function()
24619 _this.fireEvent('OverlayViewOnRemove', _this);
24622 show: function(cpx)
24624 _this.fireEvent('OverlayViewShow', _this, cpx);
24629 _this.fireEvent('OverlayViewHide', _this);
24635 fromLatLngToContainerPixel: function(event)
24637 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24640 isApplied: function()
24642 return this.getGmapContext() == false ? false : true;
24645 getGmapContext: function()
24647 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24650 GMapContext: function()
24652 var position = new google.maps.LatLng(this.latitude, this.longitude);
24654 var _map = new google.maps.Map(this.el.dom, {
24657 mapTypeId: this.mapTypeId,
24658 mapTypeControl: this.mapTypeControl,
24659 disableDoubleClickZoom: this.disableDoubleClickZoom,
24660 scrollwheel: this.scrollwheel,
24661 streetViewControl: this.streetViewControl,
24662 locationName: this.locationName,
24663 draggable: this.draggable,
24664 enableAutocomplete: this.enableAutocomplete,
24665 enableReverseGeocode: this.enableReverseGeocode
24668 var _marker = new google.maps.Marker({
24669 position: position,
24671 title: this.markerTitle,
24672 draggable: this.draggable
24679 location: position,
24680 radius: this.radius,
24681 locationName: this.locationName,
24682 addressComponents: {
24683 formatted_address: null,
24684 addressLine1: null,
24685 addressLine2: null,
24687 streetNumber: null,
24691 stateOrProvince: null
24694 domContainer: this.el.dom,
24695 geodecoder: new google.maps.Geocoder()
24699 drawCircle: function(center, radius, options)
24701 if (this.gMapContext.circle != null) {
24702 this.gMapContext.circle.setMap(null);
24706 options = Roo.apply({}, options, {
24707 strokeColor: "#0000FF",
24708 strokeOpacity: .35,
24710 fillColor: "#0000FF",
24714 options.map = this.gMapContext.map;
24715 options.radius = radius;
24716 options.center = center;
24717 this.gMapContext.circle = new google.maps.Circle(options);
24718 return this.gMapContext.circle;
24724 setPosition: function(location)
24726 this.gMapContext.location = location;
24727 this.gMapContext.marker.setPosition(location);
24728 this.gMapContext.map.panTo(location);
24729 this.drawCircle(location, this.gMapContext.radius, {});
24733 if (this.gMapContext.settings.enableReverseGeocode) {
24734 this.gMapContext.geodecoder.geocode({
24735 latLng: this.gMapContext.location
24736 }, function(results, status) {
24738 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24739 _this.gMapContext.locationName = results[0].formatted_address;
24740 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24742 _this.fireEvent('positionchanged', this, location);
24749 this.fireEvent('positionchanged', this, location);
24754 google.maps.event.trigger(this.gMapContext.map, "resize");
24756 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24758 this.fireEvent('resize', this);
24761 setPositionByLatLng: function(latitude, longitude)
24763 this.setPosition(new google.maps.LatLng(latitude, longitude));
24766 getCurrentPosition: function()
24769 latitude: this.gMapContext.location.lat(),
24770 longitude: this.gMapContext.location.lng()
24774 getAddressName: function()
24776 return this.gMapContext.locationName;
24779 getAddressComponents: function()
24781 return this.gMapContext.addressComponents;
24784 address_component_from_google_geocode: function(address_components)
24788 for (var i = 0; i < address_components.length; i++) {
24789 var component = address_components[i];
24790 if (component.types.indexOf("postal_code") >= 0) {
24791 result.postalCode = component.short_name;
24792 } else if (component.types.indexOf("street_number") >= 0) {
24793 result.streetNumber = component.short_name;
24794 } else if (component.types.indexOf("route") >= 0) {
24795 result.streetName = component.short_name;
24796 } else if (component.types.indexOf("neighborhood") >= 0) {
24797 result.city = component.short_name;
24798 } else if (component.types.indexOf("locality") >= 0) {
24799 result.city = component.short_name;
24800 } else if (component.types.indexOf("sublocality") >= 0) {
24801 result.district = component.short_name;
24802 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24803 result.stateOrProvince = component.short_name;
24804 } else if (component.types.indexOf("country") >= 0) {
24805 result.country = component.short_name;
24809 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24810 result.addressLine2 = "";
24814 setZoomLevel: function(zoom)
24816 this.gMapContext.map.setZoom(zoom);
24829 this.fireEvent('show', this);
24840 this.fireEvent('hide', this);
24845 Roo.apply(Roo.bootstrap.LocationPicker, {
24847 OverlayView : function(map, options)
24849 options = options || {};
24863 * @class Roo.bootstrap.Alert
24864 * @extends Roo.bootstrap.Component
24865 * Bootstrap Alert class
24866 * @cfg {String} title The title of alert
24867 * @cfg {String} html The content of alert
24868 * @cfg {String} weight ( success | info | warning | danger )
24869 * @cfg {String} faicon font-awesomeicon
24872 * Create a new alert
24873 * @param {Object} config The config object
24877 Roo.bootstrap.Alert = function(config){
24878 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24882 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24889 getAutoCreate : function()
24898 cls : 'roo-alert-icon'
24903 cls : 'roo-alert-title',
24908 cls : 'roo-alert-text',
24915 cfg.cn[0].cls += ' fa ' + this.faicon;
24919 cfg.cls += ' alert-' + this.weight;
24925 initEvents: function()
24927 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24930 setTitle : function(str)
24932 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24935 setText : function(str)
24937 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24940 setWeight : function(weight)
24943 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24946 this.weight = weight;
24948 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24951 setIcon : function(icon)
24954 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24957 this.faicon = icon;
24959 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24980 * @class Roo.bootstrap.UploadCropbox
24981 * @extends Roo.bootstrap.Component
24982 * Bootstrap UploadCropbox class
24983 * @cfg {String} emptyText show when image has been loaded
24984 * @cfg {String} rotateNotify show when image too small to rotate
24985 * @cfg {Number} errorTimeout default 3000
24986 * @cfg {Number} minWidth default 300
24987 * @cfg {Number} minHeight default 300
24988 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24989 * @cfg {Boolean} isDocument (true|false) default false
24990 * @cfg {String} url action url
24991 * @cfg {String} paramName default 'imageUpload'
24992 * @cfg {String} method default POST
24993 * @cfg {Boolean} loadMask (true|false) default true
24994 * @cfg {Boolean} loadingText default 'Loading...'
24997 * Create a new UploadCropbox
24998 * @param {Object} config The config object
25001 Roo.bootstrap.UploadCropbox = function(config){
25002 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25006 * @event beforeselectfile
25007 * Fire before select file
25008 * @param {Roo.bootstrap.UploadCropbox} this
25010 "beforeselectfile" : true,
25013 * Fire after initEvent
25014 * @param {Roo.bootstrap.UploadCropbox} this
25019 * Fire after initEvent
25020 * @param {Roo.bootstrap.UploadCropbox} this
25021 * @param {String} data
25026 * Fire when preparing the file data
25027 * @param {Roo.bootstrap.UploadCropbox} this
25028 * @param {Object} file
25033 * Fire when get exception
25034 * @param {Roo.bootstrap.UploadCropbox} this
25035 * @param {XMLHttpRequest} xhr
25037 "exception" : true,
25039 * @event beforeloadcanvas
25040 * Fire before load the canvas
25041 * @param {Roo.bootstrap.UploadCropbox} this
25042 * @param {String} src
25044 "beforeloadcanvas" : true,
25047 * Fire when trash image
25048 * @param {Roo.bootstrap.UploadCropbox} this
25053 * Fire when download the image
25054 * @param {Roo.bootstrap.UploadCropbox} this
25058 * @event footerbuttonclick
25059 * Fire when footerbuttonclick
25060 * @param {Roo.bootstrap.UploadCropbox} this
25061 * @param {String} type
25063 "footerbuttonclick" : true,
25067 * @param {Roo.bootstrap.UploadCropbox} this
25072 * Fire when rotate the image
25073 * @param {Roo.bootstrap.UploadCropbox} this
25074 * @param {String} pos
25079 * Fire when inspect the file
25080 * @param {Roo.bootstrap.UploadCropbox} this
25081 * @param {Object} file
25086 * Fire when xhr upload the file
25087 * @param {Roo.bootstrap.UploadCropbox} this
25088 * @param {Object} data
25093 * Fire when arrange the file data
25094 * @param {Roo.bootstrap.UploadCropbox} this
25095 * @param {Object} formData
25100 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25103 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25105 emptyText : 'Click to upload image',
25106 rotateNotify : 'Image is too small to rotate',
25107 errorTimeout : 3000,
25121 cropType : 'image/jpeg',
25123 canvasLoaded : false,
25124 isDocument : false,
25126 paramName : 'imageUpload',
25128 loadingText : 'Loading...',
25131 getAutoCreate : function()
25135 cls : 'roo-upload-cropbox',
25139 cls : 'roo-upload-cropbox-selector',
25144 cls : 'roo-upload-cropbox-body',
25145 style : 'cursor:pointer',
25149 cls : 'roo-upload-cropbox-preview'
25153 cls : 'roo-upload-cropbox-thumb'
25157 cls : 'roo-upload-cropbox-empty-notify',
25158 html : this.emptyText
25162 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25163 html : this.rotateNotify
25169 cls : 'roo-upload-cropbox-footer',
25172 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25182 onRender : function(ct, position)
25184 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25186 if (this.buttons.length) {
25188 Roo.each(this.buttons, function(bb) {
25190 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25192 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25198 this.maskEl = this.el;
25202 initEvents : function()
25204 this.urlAPI = (window.createObjectURL && window) ||
25205 (window.URL && URL.revokeObjectURL && URL) ||
25206 (window.webkitURL && webkitURL);
25208 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25209 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25211 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25212 this.selectorEl.hide();
25214 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25215 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25217 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25218 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25219 this.thumbEl.hide();
25221 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25222 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25224 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25225 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25226 this.errorEl.hide();
25228 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25229 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25230 this.footerEl.hide();
25232 this.setThumbBoxSize();
25238 this.fireEvent('initial', this);
25245 window.addEventListener("resize", function() { _this.resize(); } );
25247 this.bodyEl.on('click', this.beforeSelectFile, this);
25250 this.bodyEl.on('touchstart', this.onTouchStart, this);
25251 this.bodyEl.on('touchmove', this.onTouchMove, this);
25252 this.bodyEl.on('touchend', this.onTouchEnd, this);
25256 this.bodyEl.on('mousedown', this.onMouseDown, this);
25257 this.bodyEl.on('mousemove', this.onMouseMove, this);
25258 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25259 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25260 Roo.get(document).on('mouseup', this.onMouseUp, this);
25263 this.selectorEl.on('change', this.onFileSelected, this);
25269 this.baseScale = 1;
25271 this.baseRotate = 1;
25272 this.dragable = false;
25273 this.pinching = false;
25276 this.cropData = false;
25277 this.notifyEl.dom.innerHTML = this.emptyText;
25279 this.selectorEl.dom.value = '';
25283 resize : function()
25285 if(this.fireEvent('resize', this) != false){
25286 this.setThumbBoxPosition();
25287 this.setCanvasPosition();
25291 onFooterButtonClick : function(e, el, o, type)
25294 case 'rotate-left' :
25295 this.onRotateLeft(e);
25297 case 'rotate-right' :
25298 this.onRotateRight(e);
25301 this.beforeSelectFile(e);
25316 this.fireEvent('footerbuttonclick', this, type);
25319 beforeSelectFile : function(e)
25321 e.preventDefault();
25323 if(this.fireEvent('beforeselectfile', this) != false){
25324 this.selectorEl.dom.click();
25328 onFileSelected : function(e)
25330 e.preventDefault();
25332 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25336 var file = this.selectorEl.dom.files[0];
25338 if(this.fireEvent('inspect', this, file) != false){
25339 this.prepare(file);
25344 trash : function(e)
25346 this.fireEvent('trash', this);
25349 download : function(e)
25351 this.fireEvent('download', this);
25354 loadCanvas : function(src)
25356 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25360 this.imageEl = document.createElement('img');
25364 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25366 this.imageEl.src = src;
25370 onLoadCanvas : function()
25372 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25373 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25375 this.bodyEl.un('click', this.beforeSelectFile, this);
25377 this.notifyEl.hide();
25378 this.thumbEl.show();
25379 this.footerEl.show();
25381 this.baseRotateLevel();
25383 if(this.isDocument){
25384 this.setThumbBoxSize();
25387 this.setThumbBoxPosition();
25389 this.baseScaleLevel();
25395 this.canvasLoaded = true;
25398 this.maskEl.unmask();
25403 setCanvasPosition : function()
25405 if(!this.canvasEl){
25409 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25410 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25412 this.previewEl.setLeft(pw);
25413 this.previewEl.setTop(ph);
25417 onMouseDown : function(e)
25421 this.dragable = true;
25422 this.pinching = false;
25424 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25425 this.dragable = false;
25429 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25430 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25434 onMouseMove : function(e)
25438 if(!this.canvasLoaded){
25442 if (!this.dragable){
25446 var minX = Math.ceil(this.thumbEl.getLeft(true));
25447 var minY = Math.ceil(this.thumbEl.getTop(true));
25449 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25450 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25452 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25453 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25455 x = x - this.mouseX;
25456 y = y - this.mouseY;
25458 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25459 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25461 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25462 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25464 this.previewEl.setLeft(bgX);
25465 this.previewEl.setTop(bgY);
25467 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25468 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25471 onMouseUp : function(e)
25475 this.dragable = false;
25478 onMouseWheel : function(e)
25482 this.startScale = this.scale;
25484 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25486 if(!this.zoomable()){
25487 this.scale = this.startScale;
25496 zoomable : function()
25498 var minScale = this.thumbEl.getWidth() / this.minWidth;
25500 if(this.minWidth < this.minHeight){
25501 minScale = this.thumbEl.getHeight() / this.minHeight;
25504 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25505 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25509 (this.rotate == 0 || this.rotate == 180) &&
25511 width > this.imageEl.OriginWidth ||
25512 height > this.imageEl.OriginHeight ||
25513 (width < this.minWidth && height < this.minHeight)
25521 (this.rotate == 90 || this.rotate == 270) &&
25523 width > this.imageEl.OriginWidth ||
25524 height > this.imageEl.OriginHeight ||
25525 (width < this.minHeight && height < this.minWidth)
25532 !this.isDocument &&
25533 (this.rotate == 0 || this.rotate == 180) &&
25535 width < this.minWidth ||
25536 width > this.imageEl.OriginWidth ||
25537 height < this.minHeight ||
25538 height > this.imageEl.OriginHeight
25545 !this.isDocument &&
25546 (this.rotate == 90 || this.rotate == 270) &&
25548 width < this.minHeight ||
25549 width > this.imageEl.OriginWidth ||
25550 height < this.minWidth ||
25551 height > this.imageEl.OriginHeight
25561 onRotateLeft : function(e)
25563 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25565 var minScale = this.thumbEl.getWidth() / this.minWidth;
25567 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25568 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25570 this.startScale = this.scale;
25572 while (this.getScaleLevel() < minScale){
25574 this.scale = this.scale + 1;
25576 if(!this.zoomable()){
25581 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25582 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25587 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25594 this.scale = this.startScale;
25596 this.onRotateFail();
25601 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25603 if(this.isDocument){
25604 this.setThumbBoxSize();
25605 this.setThumbBoxPosition();
25606 this.setCanvasPosition();
25611 this.fireEvent('rotate', this, 'left');
25615 onRotateRight : function(e)
25617 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25619 var minScale = this.thumbEl.getWidth() / this.minWidth;
25621 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25622 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25624 this.startScale = this.scale;
25626 while (this.getScaleLevel() < minScale){
25628 this.scale = this.scale + 1;
25630 if(!this.zoomable()){
25635 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25636 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25641 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25648 this.scale = this.startScale;
25650 this.onRotateFail();
25655 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25657 if(this.isDocument){
25658 this.setThumbBoxSize();
25659 this.setThumbBoxPosition();
25660 this.setCanvasPosition();
25665 this.fireEvent('rotate', this, 'right');
25668 onRotateFail : function()
25670 this.errorEl.show(true);
25674 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25679 this.previewEl.dom.innerHTML = '';
25681 var canvasEl = document.createElement("canvas");
25683 var contextEl = canvasEl.getContext("2d");
25685 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25686 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25687 var center = this.imageEl.OriginWidth / 2;
25689 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25690 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25691 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25692 center = this.imageEl.OriginHeight / 2;
25695 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25697 contextEl.translate(center, center);
25698 contextEl.rotate(this.rotate * Math.PI / 180);
25700 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25702 this.canvasEl = document.createElement("canvas");
25704 this.contextEl = this.canvasEl.getContext("2d");
25706 switch (this.rotate) {
25709 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25710 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25712 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25717 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25718 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25720 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25721 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);
25725 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25730 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25731 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25733 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25734 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);
25738 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);
25743 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25744 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25746 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25747 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25751 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);
25758 this.previewEl.appendChild(this.canvasEl);
25760 this.setCanvasPosition();
25765 if(!this.canvasLoaded){
25769 var imageCanvas = document.createElement("canvas");
25771 var imageContext = imageCanvas.getContext("2d");
25773 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25774 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25776 var center = imageCanvas.width / 2;
25778 imageContext.translate(center, center);
25780 imageContext.rotate(this.rotate * Math.PI / 180);
25782 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25784 var canvas = document.createElement("canvas");
25786 var context = canvas.getContext("2d");
25788 canvas.width = this.minWidth;
25789 canvas.height = this.minHeight;
25791 switch (this.rotate) {
25794 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25795 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25797 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25798 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25800 var targetWidth = this.minWidth - 2 * x;
25801 var targetHeight = this.minHeight - 2 * y;
25805 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25806 scale = targetWidth / width;
25809 if(x > 0 && y == 0){
25810 scale = targetHeight / height;
25813 if(x > 0 && y > 0){
25814 scale = targetWidth / width;
25816 if(width < height){
25817 scale = targetHeight / height;
25821 context.scale(scale, scale);
25823 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25824 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25826 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25827 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25829 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25834 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25835 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25837 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25838 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25840 var targetWidth = this.minWidth - 2 * x;
25841 var targetHeight = this.minHeight - 2 * y;
25845 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25846 scale = targetWidth / width;
25849 if(x > 0 && y == 0){
25850 scale = targetHeight / height;
25853 if(x > 0 && y > 0){
25854 scale = targetWidth / width;
25856 if(width < height){
25857 scale = targetHeight / height;
25861 context.scale(scale, scale);
25863 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25864 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25866 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25867 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25869 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25871 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25876 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25877 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25879 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25880 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25882 var targetWidth = this.minWidth - 2 * x;
25883 var targetHeight = this.minHeight - 2 * y;
25887 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25888 scale = targetWidth / width;
25891 if(x > 0 && y == 0){
25892 scale = targetHeight / height;
25895 if(x > 0 && y > 0){
25896 scale = targetWidth / width;
25898 if(width < height){
25899 scale = targetHeight / height;
25903 context.scale(scale, scale);
25905 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25906 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25908 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25909 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25911 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25912 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25914 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25919 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25920 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25922 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25923 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25925 var targetWidth = this.minWidth - 2 * x;
25926 var targetHeight = this.minHeight - 2 * y;
25930 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25931 scale = targetWidth / width;
25934 if(x > 0 && y == 0){
25935 scale = targetHeight / height;
25938 if(x > 0 && y > 0){
25939 scale = targetWidth / width;
25941 if(width < height){
25942 scale = targetHeight / height;
25946 context.scale(scale, scale);
25948 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25949 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25951 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25952 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25954 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25956 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25963 this.cropData = canvas.toDataURL(this.cropType);
25965 if(this.fireEvent('crop', this, this.cropData) !== false){
25966 this.process(this.file, this.cropData);
25973 setThumbBoxSize : function()
25977 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25978 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25979 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25981 this.minWidth = width;
25982 this.minHeight = height;
25984 if(this.rotate == 90 || this.rotate == 270){
25985 this.minWidth = height;
25986 this.minHeight = width;
25991 width = Math.ceil(this.minWidth * height / this.minHeight);
25993 if(this.minWidth > this.minHeight){
25995 height = Math.ceil(this.minHeight * width / this.minWidth);
25998 this.thumbEl.setStyle({
25999 width : width + 'px',
26000 height : height + 'px'
26007 setThumbBoxPosition : function()
26009 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26010 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26012 this.thumbEl.setLeft(x);
26013 this.thumbEl.setTop(y);
26017 baseRotateLevel : function()
26019 this.baseRotate = 1;
26022 typeof(this.exif) != 'undefined' &&
26023 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26024 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26026 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26029 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26033 baseScaleLevel : function()
26037 if(this.isDocument){
26039 if(this.baseRotate == 6 || this.baseRotate == 8){
26041 height = this.thumbEl.getHeight();
26042 this.baseScale = height / this.imageEl.OriginWidth;
26044 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26045 width = this.thumbEl.getWidth();
26046 this.baseScale = width / this.imageEl.OriginHeight;
26052 height = this.thumbEl.getHeight();
26053 this.baseScale = height / this.imageEl.OriginHeight;
26055 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26056 width = this.thumbEl.getWidth();
26057 this.baseScale = width / this.imageEl.OriginWidth;
26063 if(this.baseRotate == 6 || this.baseRotate == 8){
26065 width = this.thumbEl.getHeight();
26066 this.baseScale = width / this.imageEl.OriginHeight;
26068 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26069 height = this.thumbEl.getWidth();
26070 this.baseScale = height / this.imageEl.OriginHeight;
26073 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26074 height = this.thumbEl.getWidth();
26075 this.baseScale = height / this.imageEl.OriginHeight;
26077 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26078 width = this.thumbEl.getHeight();
26079 this.baseScale = width / this.imageEl.OriginWidth;
26086 width = this.thumbEl.getWidth();
26087 this.baseScale = width / this.imageEl.OriginWidth;
26089 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26090 height = this.thumbEl.getHeight();
26091 this.baseScale = height / this.imageEl.OriginHeight;
26094 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26096 height = this.thumbEl.getHeight();
26097 this.baseScale = height / this.imageEl.OriginHeight;
26099 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26100 width = this.thumbEl.getWidth();
26101 this.baseScale = width / this.imageEl.OriginWidth;
26109 getScaleLevel : function()
26111 return this.baseScale * Math.pow(1.1, this.scale);
26114 onTouchStart : function(e)
26116 if(!this.canvasLoaded){
26117 this.beforeSelectFile(e);
26121 var touches = e.browserEvent.touches;
26127 if(touches.length == 1){
26128 this.onMouseDown(e);
26132 if(touches.length != 2){
26138 for(var i = 0, finger; finger = touches[i]; i++){
26139 coords.push(finger.pageX, finger.pageY);
26142 var x = Math.pow(coords[0] - coords[2], 2);
26143 var y = Math.pow(coords[1] - coords[3], 2);
26145 this.startDistance = Math.sqrt(x + y);
26147 this.startScale = this.scale;
26149 this.pinching = true;
26150 this.dragable = false;
26154 onTouchMove : function(e)
26156 if(!this.pinching && !this.dragable){
26160 var touches = e.browserEvent.touches;
26167 this.onMouseMove(e);
26173 for(var i = 0, finger; finger = touches[i]; i++){
26174 coords.push(finger.pageX, finger.pageY);
26177 var x = Math.pow(coords[0] - coords[2], 2);
26178 var y = Math.pow(coords[1] - coords[3], 2);
26180 this.endDistance = Math.sqrt(x + y);
26182 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26184 if(!this.zoomable()){
26185 this.scale = this.startScale;
26193 onTouchEnd : function(e)
26195 this.pinching = false;
26196 this.dragable = false;
26200 process : function(file, crop)
26203 this.maskEl.mask(this.loadingText);
26206 this.xhr = new XMLHttpRequest();
26208 file.xhr = this.xhr;
26210 this.xhr.open(this.method, this.url, true);
26213 "Accept": "application/json",
26214 "Cache-Control": "no-cache",
26215 "X-Requested-With": "XMLHttpRequest"
26218 for (var headerName in headers) {
26219 var headerValue = headers[headerName];
26221 this.xhr.setRequestHeader(headerName, headerValue);
26227 this.xhr.onload = function()
26229 _this.xhrOnLoad(_this.xhr);
26232 this.xhr.onerror = function()
26234 _this.xhrOnError(_this.xhr);
26237 var formData = new FormData();
26239 formData.append('returnHTML', 'NO');
26242 formData.append('crop', crop);
26245 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26246 formData.append(this.paramName, file, file.name);
26249 if(typeof(file.filename) != 'undefined'){
26250 formData.append('filename', file.filename);
26253 if(typeof(file.mimetype) != 'undefined'){
26254 formData.append('mimetype', file.mimetype);
26257 if(this.fireEvent('arrange', this, formData) != false){
26258 this.xhr.send(formData);
26262 xhrOnLoad : function(xhr)
26265 this.maskEl.unmask();
26268 if (xhr.readyState !== 4) {
26269 this.fireEvent('exception', this, xhr);
26273 var response = Roo.decode(xhr.responseText);
26275 if(!response.success){
26276 this.fireEvent('exception', this, xhr);
26280 var response = Roo.decode(xhr.responseText);
26282 this.fireEvent('upload', this, response);
26286 xhrOnError : function()
26289 this.maskEl.unmask();
26292 Roo.log('xhr on error');
26294 var response = Roo.decode(xhr.responseText);
26300 prepare : function(file)
26303 this.maskEl.mask(this.loadingText);
26309 if(typeof(file) === 'string'){
26310 this.loadCanvas(file);
26314 if(!file || !this.urlAPI){
26319 this.cropType = file.type;
26323 if(this.fireEvent('prepare', this, this.file) != false){
26325 var reader = new FileReader();
26327 reader.onload = function (e) {
26328 if (e.target.error) {
26329 Roo.log(e.target.error);
26333 var buffer = e.target.result,
26334 dataView = new DataView(buffer),
26336 maxOffset = dataView.byteLength - 4,
26340 if (dataView.getUint16(0) === 0xffd8) {
26341 while (offset < maxOffset) {
26342 markerBytes = dataView.getUint16(offset);
26344 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26345 markerLength = dataView.getUint16(offset + 2) + 2;
26346 if (offset + markerLength > dataView.byteLength) {
26347 Roo.log('Invalid meta data: Invalid segment size.');
26351 if(markerBytes == 0xffe1){
26352 _this.parseExifData(
26359 offset += markerLength;
26369 var url = _this.urlAPI.createObjectURL(_this.file);
26371 _this.loadCanvas(url);
26376 reader.readAsArrayBuffer(this.file);
26382 parseExifData : function(dataView, offset, length)
26384 var tiffOffset = offset + 10,
26388 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26389 // No Exif data, might be XMP data instead
26393 // Check for the ASCII code for "Exif" (0x45786966):
26394 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26395 // No Exif data, might be XMP data instead
26398 if (tiffOffset + 8 > dataView.byteLength) {
26399 Roo.log('Invalid Exif data: Invalid segment size.');
26402 // Check for the two null bytes:
26403 if (dataView.getUint16(offset + 8) !== 0x0000) {
26404 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26407 // Check the byte alignment:
26408 switch (dataView.getUint16(tiffOffset)) {
26410 littleEndian = true;
26413 littleEndian = false;
26416 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26419 // Check for the TIFF tag marker (0x002A):
26420 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26421 Roo.log('Invalid Exif data: Missing TIFF marker.');
26424 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26425 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26427 this.parseExifTags(
26430 tiffOffset + dirOffset,
26435 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26440 if (dirOffset + 6 > dataView.byteLength) {
26441 Roo.log('Invalid Exif data: Invalid directory offset.');
26444 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26445 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26446 if (dirEndOffset + 4 > dataView.byteLength) {
26447 Roo.log('Invalid Exif data: Invalid directory size.');
26450 for (i = 0; i < tagsNumber; i += 1) {
26454 dirOffset + 2 + 12 * i, // tag offset
26458 // Return the offset to the next directory:
26459 return dataView.getUint32(dirEndOffset, littleEndian);
26462 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26464 var tag = dataView.getUint16(offset, littleEndian);
26466 this.exif[tag] = this.getExifValue(
26470 dataView.getUint16(offset + 2, littleEndian), // tag type
26471 dataView.getUint32(offset + 4, littleEndian), // tag length
26476 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26478 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26487 Roo.log('Invalid Exif data: Invalid tag type.');
26491 tagSize = tagType.size * length;
26492 // Determine if the value is contained in the dataOffset bytes,
26493 // or if the value at the dataOffset is a pointer to the actual data:
26494 dataOffset = tagSize > 4 ?
26495 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26496 if (dataOffset + tagSize > dataView.byteLength) {
26497 Roo.log('Invalid Exif data: Invalid data offset.');
26500 if (length === 1) {
26501 return tagType.getValue(dataView, dataOffset, littleEndian);
26504 for (i = 0; i < length; i += 1) {
26505 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26508 if (tagType.ascii) {
26510 // Concatenate the chars:
26511 for (i = 0; i < values.length; i += 1) {
26513 // Ignore the terminating NULL byte(s):
26514 if (c === '\u0000') {
26526 Roo.apply(Roo.bootstrap.UploadCropbox, {
26528 'Orientation': 0x0112
26532 1: 0, //'top-left',
26534 3: 180, //'bottom-right',
26535 // 4: 'bottom-left',
26537 6: 90, //'right-top',
26538 // 7: 'right-bottom',
26539 8: 270 //'left-bottom'
26543 // byte, 8-bit unsigned int:
26545 getValue: function (dataView, dataOffset) {
26546 return dataView.getUint8(dataOffset);
26550 // ascii, 8-bit byte:
26552 getValue: function (dataView, dataOffset) {
26553 return String.fromCharCode(dataView.getUint8(dataOffset));
26558 // short, 16 bit int:
26560 getValue: function (dataView, dataOffset, littleEndian) {
26561 return dataView.getUint16(dataOffset, littleEndian);
26565 // long, 32 bit int:
26567 getValue: function (dataView, dataOffset, littleEndian) {
26568 return dataView.getUint32(dataOffset, littleEndian);
26572 // rational = two long values, first is numerator, second is denominator:
26574 getValue: function (dataView, dataOffset, littleEndian) {
26575 return dataView.getUint32(dataOffset, littleEndian) /
26576 dataView.getUint32(dataOffset + 4, littleEndian);
26580 // slong, 32 bit signed int:
26582 getValue: function (dataView, dataOffset, littleEndian) {
26583 return dataView.getInt32(dataOffset, littleEndian);
26587 // srational, two slongs, first is numerator, second is denominator:
26589 getValue: function (dataView, dataOffset, littleEndian) {
26590 return dataView.getInt32(dataOffset, littleEndian) /
26591 dataView.getInt32(dataOffset + 4, littleEndian);
26601 cls : 'btn-group roo-upload-cropbox-rotate-left',
26602 action : 'rotate-left',
26606 cls : 'btn btn-default',
26607 html : '<i class="fa fa-undo"></i>'
26613 cls : 'btn-group roo-upload-cropbox-picture',
26614 action : 'picture',
26618 cls : 'btn btn-default',
26619 html : '<i class="fa fa-picture-o"></i>'
26625 cls : 'btn-group roo-upload-cropbox-rotate-right',
26626 action : 'rotate-right',
26630 cls : 'btn btn-default',
26631 html : '<i class="fa fa-repeat"></i>'
26639 cls : 'btn-group roo-upload-cropbox-rotate-left',
26640 action : 'rotate-left',
26644 cls : 'btn btn-default',
26645 html : '<i class="fa fa-undo"></i>'
26651 cls : 'btn-group roo-upload-cropbox-download',
26652 action : 'download',
26656 cls : 'btn btn-default',
26657 html : '<i class="fa fa-download"></i>'
26663 cls : 'btn-group roo-upload-cropbox-crop',
26668 cls : 'btn btn-default',
26669 html : '<i class="fa fa-crop"></i>'
26675 cls : 'btn-group roo-upload-cropbox-trash',
26680 cls : 'btn btn-default',
26681 html : '<i class="fa fa-trash"></i>'
26687 cls : 'btn-group roo-upload-cropbox-rotate-right',
26688 action : 'rotate-right',
26692 cls : 'btn btn-default',
26693 html : '<i class="fa fa-repeat"></i>'
26701 cls : 'btn-group roo-upload-cropbox-rotate-left',
26702 action : 'rotate-left',
26706 cls : 'btn btn-default',
26707 html : '<i class="fa fa-undo"></i>'
26713 cls : 'btn-group roo-upload-cropbox-rotate-right',
26714 action : 'rotate-right',
26718 cls : 'btn btn-default',
26719 html : '<i class="fa fa-repeat"></i>'
26732 * @class Roo.bootstrap.DocumentManager
26733 * @extends Roo.bootstrap.Component
26734 * Bootstrap DocumentManager class
26735 * @cfg {String} paramName default 'imageUpload'
26736 * @cfg {String} method default POST
26737 * @cfg {String} url action url
26738 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26739 * @cfg {Boolean} multiple multiple upload default true
26740 * @cfg {Number} thumbSize default 300
26741 * @cfg {String} fieldLabel
26742 * @cfg {Number} labelWidth default 4
26743 * @cfg {String} labelAlign (left|top) default left
26744 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26747 * Create a new DocumentManager
26748 * @param {Object} config The config object
26751 Roo.bootstrap.DocumentManager = function(config){
26752 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26757 * Fire when initial the DocumentManager
26758 * @param {Roo.bootstrap.DocumentManager} this
26763 * inspect selected file
26764 * @param {Roo.bootstrap.DocumentManager} this
26765 * @param {File} file
26770 * Fire when xhr load exception
26771 * @param {Roo.bootstrap.DocumentManager} this
26772 * @param {XMLHttpRequest} xhr
26774 "exception" : true,
26777 * prepare the form data
26778 * @param {Roo.bootstrap.DocumentManager} this
26779 * @param {Object} formData
26784 * Fire when remove the file
26785 * @param {Roo.bootstrap.DocumentManager} this
26786 * @param {Object} file
26791 * Fire after refresh the file
26792 * @param {Roo.bootstrap.DocumentManager} this
26797 * Fire after click the image
26798 * @param {Roo.bootstrap.DocumentManager} this
26799 * @param {Object} file
26804 * Fire when upload a image and editable set to true
26805 * @param {Roo.bootstrap.DocumentManager} this
26806 * @param {Object} file
26810 * @event beforeselectfile
26811 * Fire before select file
26812 * @param {Roo.bootstrap.DocumentManager} this
26814 "beforeselectfile" : true,
26817 * Fire before process file
26818 * @param {Roo.bootstrap.DocumentManager} this
26819 * @param {Object} file
26826 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26835 paramName : 'imageUpload',
26838 labelAlign : 'left',
26845 getAutoCreate : function()
26847 var managerWidget = {
26849 cls : 'roo-document-manager',
26853 cls : 'roo-document-manager-selector',
26858 cls : 'roo-document-manager-uploader',
26862 cls : 'roo-document-manager-upload-btn',
26863 html : '<i class="fa fa-plus"></i>'
26874 cls : 'column col-md-12',
26879 if(this.fieldLabel.length){
26884 cls : 'column col-md-12',
26885 html : this.fieldLabel
26889 cls : 'column col-md-12',
26894 if(this.labelAlign == 'left'){
26898 cls : 'column col-md-' + this.labelWidth,
26899 html : this.fieldLabel
26903 cls : 'column col-md-' + (12 - this.labelWidth),
26913 cls : 'row clearfix',
26921 initEvents : function()
26923 this.managerEl = this.el.select('.roo-document-manager', true).first();
26924 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26926 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26927 this.selectorEl.hide();
26930 this.selectorEl.attr('multiple', 'multiple');
26933 this.selectorEl.on('change', this.onFileSelected, this);
26935 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26936 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26938 this.uploader.on('click', this.onUploaderClick, this);
26940 this.renderProgressDialog();
26944 window.addEventListener("resize", function() { _this.refresh(); } );
26946 this.fireEvent('initial', this);
26949 renderProgressDialog : function()
26953 this.progressDialog = new Roo.bootstrap.Modal({
26954 cls : 'roo-document-manager-progress-dialog',
26955 allow_close : false,
26965 btnclick : function() {
26966 _this.uploadCancel();
26972 this.progressDialog.render(Roo.get(document.body));
26974 this.progress = new Roo.bootstrap.Progress({
26975 cls : 'roo-document-manager-progress',
26980 this.progress.render(this.progressDialog.getChildContainer());
26982 this.progressBar = new Roo.bootstrap.ProgressBar({
26983 cls : 'roo-document-manager-progress-bar',
26986 aria_valuemax : 12,
26990 this.progressBar.render(this.progress.getChildContainer());
26993 onUploaderClick : function(e)
26995 e.preventDefault();
26997 if(this.fireEvent('beforeselectfile', this) != false){
26998 this.selectorEl.dom.click();
27003 onFileSelected : function(e)
27005 e.preventDefault();
27007 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27011 Roo.each(this.selectorEl.dom.files, function(file){
27012 if(this.fireEvent('inspect', this, file) != false){
27013 this.files.push(file);
27023 this.selectorEl.dom.value = '';
27025 if(!this.files.length){
27029 if(this.boxes > 0 && this.files.length > this.boxes){
27030 this.files = this.files.slice(0, this.boxes);
27033 this.uploader.show();
27035 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27036 this.uploader.hide();
27045 Roo.each(this.files, function(file){
27047 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27048 var f = this.renderPreview(file);
27053 if(file.type.indexOf('image') != -1){
27054 this.delegates.push(
27056 _this.process(file);
27057 }).createDelegate(this)
27065 _this.process(file);
27066 }).createDelegate(this)
27071 this.files = files;
27073 this.delegates = this.delegates.concat(docs);
27075 if(!this.delegates.length){
27080 this.progressBar.aria_valuemax = this.delegates.length;
27087 arrange : function()
27089 if(!this.delegates.length){
27090 this.progressDialog.hide();
27095 var delegate = this.delegates.shift();
27097 this.progressDialog.show();
27099 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27101 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27106 refresh : function()
27108 this.uploader.show();
27110 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27111 this.uploader.hide();
27114 Roo.isTouch ? this.closable(false) : this.closable(true);
27116 this.fireEvent('refresh', this);
27119 onRemove : function(e, el, o)
27121 e.preventDefault();
27123 this.fireEvent('remove', this, o);
27127 remove : function(o)
27131 Roo.each(this.files, function(file){
27132 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27141 this.files = files;
27148 Roo.each(this.files, function(file){
27153 file.target.remove();
27162 onClick : function(e, el, o)
27164 e.preventDefault();
27166 this.fireEvent('click', this, o);
27170 closable : function(closable)
27172 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27174 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27186 xhrOnLoad : function(xhr)
27188 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27192 if (xhr.readyState !== 4) {
27194 this.fireEvent('exception', this, xhr);
27198 var response = Roo.decode(xhr.responseText);
27200 if(!response.success){
27202 this.fireEvent('exception', this, xhr);
27206 var file = this.renderPreview(response.data);
27208 this.files.push(file);
27214 xhrOnError : function(xhr)
27216 Roo.log('xhr on error');
27218 var response = Roo.decode(xhr.responseText);
27225 process : function(file)
27227 if(this.fireEvent('process', this, file) !== false){
27228 if(this.editable && file.type.indexOf('image') != -1){
27229 this.fireEvent('edit', this, file);
27233 this.uploadStart(file, false);
27240 uploadStart : function(file, crop)
27242 this.xhr = new XMLHttpRequest();
27244 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27249 file.xhr = this.xhr;
27251 this.managerEl.createChild({
27253 cls : 'roo-document-manager-loading',
27257 tooltip : file.name,
27258 cls : 'roo-document-manager-thumb',
27259 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27265 this.xhr.open(this.method, this.url, true);
27268 "Accept": "application/json",
27269 "Cache-Control": "no-cache",
27270 "X-Requested-With": "XMLHttpRequest"
27273 for (var headerName in headers) {
27274 var headerValue = headers[headerName];
27276 this.xhr.setRequestHeader(headerName, headerValue);
27282 this.xhr.onload = function()
27284 _this.xhrOnLoad(_this.xhr);
27287 this.xhr.onerror = function()
27289 _this.xhrOnError(_this.xhr);
27292 var formData = new FormData();
27294 formData.append('returnHTML', 'NO');
27297 formData.append('crop', crop);
27300 formData.append(this.paramName, file, file.name);
27302 if(this.fireEvent('prepare', this, formData) != false){
27303 this.xhr.send(formData);
27307 uploadCancel : function()
27314 this.delegates = [];
27316 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27323 renderPreview : function(file)
27325 if(typeof(file.target) != 'undefined' && file.target){
27329 var previewEl = this.managerEl.createChild({
27331 cls : 'roo-document-manager-preview',
27335 tooltip : file.filename,
27336 cls : 'roo-document-manager-thumb',
27337 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27342 html : '<i class="fa fa-times-circle"></i>'
27347 var close = previewEl.select('button.close', true).first();
27349 close.on('click', this.onRemove, this, file);
27351 file.target = previewEl;
27353 var image = previewEl.select('img', true).first();
27357 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27359 image.on('click', this.onClick, this, file);
27365 onPreviewLoad : function(file, image)
27367 if(typeof(file.target) == 'undefined' || !file.target){
27371 var width = image.dom.naturalWidth || image.dom.width;
27372 var height = image.dom.naturalHeight || image.dom.height;
27374 if(width > height){
27375 file.target.addClass('wide');
27379 file.target.addClass('tall');
27384 uploadFromSource : function(file, crop)
27386 this.xhr = new XMLHttpRequest();
27388 this.managerEl.createChild({
27390 cls : 'roo-document-manager-loading',
27394 tooltip : file.name,
27395 cls : 'roo-document-manager-thumb',
27396 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27402 this.xhr.open(this.method, this.url, true);
27405 "Accept": "application/json",
27406 "Cache-Control": "no-cache",
27407 "X-Requested-With": "XMLHttpRequest"
27410 for (var headerName in headers) {
27411 var headerValue = headers[headerName];
27413 this.xhr.setRequestHeader(headerName, headerValue);
27419 this.xhr.onload = function()
27421 _this.xhrOnLoad(_this.xhr);
27424 this.xhr.onerror = function()
27426 _this.xhrOnError(_this.xhr);
27429 var formData = new FormData();
27431 formData.append('returnHTML', 'NO');
27433 formData.append('crop', crop);
27435 if(typeof(file.filename) != 'undefined'){
27436 formData.append('filename', file.filename);
27439 if(typeof(file.mimetype) != 'undefined'){
27440 formData.append('mimetype', file.mimetype);
27443 if(this.fireEvent('prepare', this, formData) != false){
27444 this.xhr.send(formData);
27454 * @class Roo.bootstrap.DocumentViewer
27455 * @extends Roo.bootstrap.Component
27456 * Bootstrap DocumentViewer class
27459 * Create a new DocumentViewer
27460 * @param {Object} config The config object
27463 Roo.bootstrap.DocumentViewer = function(config){
27464 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27469 * Fire after initEvent
27470 * @param {Roo.bootstrap.DocumentViewer} this
27476 * @param {Roo.bootstrap.DocumentViewer} this
27481 * Fire after trash button
27482 * @param {Roo.bootstrap.DocumentViewer} this
27489 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27491 getAutoCreate : function()
27495 cls : 'roo-document-viewer',
27499 cls : 'roo-document-viewer-body',
27503 cls : 'roo-document-viewer-thumb',
27507 cls : 'roo-document-viewer-image'
27515 cls : 'roo-document-viewer-footer',
27518 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27526 cls : 'btn btn-default roo-document-viewer-trash',
27527 html : '<i class="fa fa-trash"></i>'
27540 initEvents : function()
27543 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27544 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27546 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27547 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27549 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27550 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27552 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27553 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27555 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27556 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27558 this.bodyEl.on('click', this.onClick, this);
27560 this.trashBtn.on('click', this.onTrash, this);
27564 initial : function()
27566 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27569 this.fireEvent('initial', this);
27573 onClick : function(e)
27575 e.preventDefault();
27577 this.fireEvent('click', this);
27580 onTrash : function(e)
27582 e.preventDefault();
27584 this.fireEvent('trash', this);
27596 * @class Roo.bootstrap.NavProgressBar
27597 * @extends Roo.bootstrap.Component
27598 * Bootstrap NavProgressBar class
27601 * Create a new nav progress bar
27602 * @param {Object} config The config object
27605 Roo.bootstrap.NavProgressBar = function(config){
27606 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27608 this.bullets = this.bullets || [];
27610 // Roo.bootstrap.NavProgressBar.register(this);
27614 * Fires when the active item changes
27615 * @param {Roo.bootstrap.NavProgressBar} this
27616 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27617 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27624 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27629 getAutoCreate : function()
27631 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27635 cls : 'roo-navigation-bar-group',
27639 cls : 'roo-navigation-top-bar'
27643 cls : 'roo-navigation-bullets-bar',
27647 cls : 'roo-navigation-bar'
27654 cls : 'roo-navigation-bottom-bar'
27664 initEvents: function()
27669 onRender : function(ct, position)
27671 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27673 if(this.bullets.length){
27674 Roo.each(this.bullets, function(b){
27683 addItem : function(cfg)
27685 var item = new Roo.bootstrap.NavProgressItem(cfg);
27687 item.parentId = this.id;
27688 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27691 var top = new Roo.bootstrap.Element({
27693 cls : 'roo-navigation-bar-text'
27696 var bottom = new Roo.bootstrap.Element({
27698 cls : 'roo-navigation-bar-text'
27701 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27702 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27704 var topText = new Roo.bootstrap.Element({
27706 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27709 var bottomText = new Roo.bootstrap.Element({
27711 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27714 topText.onRender(top.el, null);
27715 bottomText.onRender(bottom.el, null);
27718 item.bottomEl = bottom;
27721 this.barItems.push(item);
27726 getActive : function()
27728 var active = false;
27730 Roo.each(this.barItems, function(v){
27732 if (!v.isActive()) {
27744 setActiveItem : function(item)
27748 Roo.each(this.barItems, function(v){
27749 if (v.rid == item.rid) {
27753 if (v.isActive()) {
27754 v.setActive(false);
27759 item.setActive(true);
27761 this.fireEvent('changed', this, item, prev);
27764 getBarItem: function(rid)
27768 Roo.each(this.barItems, function(e) {
27769 if (e.rid != rid) {
27780 indexOfItem : function(item)
27784 Roo.each(this.barItems, function(v, i){
27786 if (v.rid != item.rid) {
27797 setActiveNext : function()
27799 var i = this.indexOfItem(this.getActive());
27801 if (i > this.barItems.length) {
27805 this.setActiveItem(this.barItems[i+1]);
27808 setActivePrev : function()
27810 var i = this.indexOfItem(this.getActive());
27816 this.setActiveItem(this.barItems[i-1]);
27819 format : function()
27821 if(!this.barItems.length){
27825 var width = 100 / this.barItems.length;
27827 Roo.each(this.barItems, function(i){
27828 i.el.setStyle('width', width + '%');
27829 i.topEl.el.setStyle('width', width + '%');
27830 i.bottomEl.el.setStyle('width', width + '%');
27839 * Nav Progress Item
27844 * @class Roo.bootstrap.NavProgressItem
27845 * @extends Roo.bootstrap.Component
27846 * Bootstrap NavProgressItem class
27847 * @cfg {String} rid the reference id
27848 * @cfg {Boolean} active (true|false) Is item active default false
27849 * @cfg {Boolean} disabled (true|false) Is item active default false
27850 * @cfg {String} html
27851 * @cfg {String} position (top|bottom) text position default bottom
27852 * @cfg {String} icon show icon instead of number
27855 * Create a new NavProgressItem
27856 * @param {Object} config The config object
27858 Roo.bootstrap.NavProgressItem = function(config){
27859 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27864 * The raw click event for the entire grid.
27865 * @param {Roo.bootstrap.NavProgressItem} this
27866 * @param {Roo.EventObject} e
27873 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27879 position : 'bottom',
27882 getAutoCreate : function()
27884 var iconCls = 'roo-navigation-bar-item-icon';
27886 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27890 cls: 'roo-navigation-bar-item',
27900 cfg.cls += ' active';
27903 cfg.cls += ' disabled';
27909 disable : function()
27911 this.setDisabled(true);
27914 enable : function()
27916 this.setDisabled(false);
27919 initEvents: function()
27921 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27923 this.iconEl.on('click', this.onClick, this);
27926 onClick : function(e)
27928 e.preventDefault();
27934 if(this.fireEvent('click', this, e) === false){
27938 this.parent().setActiveItem(this);
27941 isActive: function ()
27943 return this.active;
27946 setActive : function(state)
27948 if(this.active == state){
27952 this.active = state;
27955 this.el.addClass('active');
27959 this.el.removeClass('active');
27964 setDisabled : function(state)
27966 if(this.disabled == state){
27970 this.disabled = state;
27973 this.el.addClass('disabled');
27977 this.el.removeClass('disabled');
27980 tooltipEl : function()
27982 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27995 * @class Roo.bootstrap.FieldLabel
27996 * @extends Roo.bootstrap.Component
27997 * Bootstrap FieldLabel class
27998 * @cfg {String} html contents of the element
27999 * @cfg {String} tag tag of the element default label
28000 * @cfg {String} cls class of the element
28001 * @cfg {String} target label target
28002 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28003 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28004 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28005 * @cfg {String} iconTooltip default "This field is required"
28008 * Create a new FieldLabel
28009 * @param {Object} config The config object
28012 Roo.bootstrap.FieldLabel = function(config){
28013 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28018 * Fires after the field has been marked as invalid.
28019 * @param {Roo.form.FieldLabel} this
28020 * @param {String} msg The validation message
28025 * Fires after the field has been validated with no errors.
28026 * @param {Roo.form.FieldLabel} this
28032 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28039 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28040 validClass : 'text-success fa fa-lg fa-check',
28041 iconTooltip : 'This field is required',
28043 getAutoCreate : function(){
28047 cls : 'roo-bootstrap-field-label ' + this.cls,
28053 tooltip : this.iconTooltip
28065 initEvents: function()
28067 Roo.bootstrap.Element.superclass.initEvents.call(this);
28069 this.iconEl = this.el.select('i', true).first();
28071 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28073 Roo.bootstrap.FieldLabel.register(this);
28077 * Mark this field as valid
28079 markValid : function()
28081 this.iconEl.show();
28083 this.iconEl.removeClass(this.invalidClass);
28085 this.iconEl.addClass(this.validClass);
28087 this.fireEvent('valid', this);
28091 * Mark this field as invalid
28092 * @param {String} msg The validation message
28094 markInvalid : function(msg)
28096 this.iconEl.show();
28098 this.iconEl.removeClass(this.validClass);
28100 this.iconEl.addClass(this.invalidClass);
28102 this.fireEvent('invalid', this, msg);
28108 Roo.apply(Roo.bootstrap.FieldLabel, {
28113 * register a FieldLabel Group
28114 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28116 register : function(label)
28118 if(this.groups.hasOwnProperty(label.target)){
28122 this.groups[label.target] = label;
28126 * fetch a FieldLabel Group based on the target
28127 * @param {string} target
28128 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28130 get: function(target) {
28131 if (typeof(this.groups[target]) == 'undefined') {
28135 return this.groups[target] ;
28144 * page DateSplitField.
28150 * @class Roo.bootstrap.DateSplitField
28151 * @extends Roo.bootstrap.Component
28152 * Bootstrap DateSplitField class
28153 * @cfg {string} fieldLabel - the label associated
28154 * @cfg {Number} labelWidth set the width of label (0-12)
28155 * @cfg {String} labelAlign (top|left)
28156 * @cfg {Boolean} dayAllowBlank (true|false) default false
28157 * @cfg {Boolean} monthAllowBlank (true|false) default false
28158 * @cfg {Boolean} yearAllowBlank (true|false) default false
28159 * @cfg {string} dayPlaceholder
28160 * @cfg {string} monthPlaceholder
28161 * @cfg {string} yearPlaceholder
28162 * @cfg {string} dayFormat default 'd'
28163 * @cfg {string} monthFormat default 'm'
28164 * @cfg {string} yearFormat default 'Y'
28168 * Create a new DateSplitField
28169 * @param {Object} config The config object
28172 Roo.bootstrap.DateSplitField = function(config){
28173 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28179 * getting the data of years
28180 * @param {Roo.bootstrap.DateSplitField} this
28181 * @param {Object} years
28186 * getting the data of days
28187 * @param {Roo.bootstrap.DateSplitField} this
28188 * @param {Object} days
28193 * Fires after the field has been marked as invalid.
28194 * @param {Roo.form.Field} this
28195 * @param {String} msg The validation message
28200 * Fires after the field has been validated with no errors.
28201 * @param {Roo.form.Field} this
28207 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28210 labelAlign : 'top',
28212 dayAllowBlank : false,
28213 monthAllowBlank : false,
28214 yearAllowBlank : false,
28215 dayPlaceholder : '',
28216 monthPlaceholder : '',
28217 yearPlaceholder : '',
28221 isFormField : true,
28223 getAutoCreate : function()
28227 cls : 'row roo-date-split-field-group',
28232 cls : 'form-hidden-field roo-date-split-field-group-value',
28238 if(this.fieldLabel){
28241 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28245 html : this.fieldLabel
28251 Roo.each(['day', 'month', 'year'], function(t){
28254 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28261 inputEl: function ()
28263 return this.el.select('.roo-date-split-field-group-value', true).first();
28266 onRender : function(ct, position)
28270 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28272 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28274 this.dayField = new Roo.bootstrap.ComboBox({
28275 allowBlank : this.dayAllowBlank,
28276 alwaysQuery : true,
28277 displayField : 'value',
28280 forceSelection : true,
28282 placeholder : this.dayPlaceholder,
28283 selectOnFocus : true,
28284 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28285 triggerAction : 'all',
28287 valueField : 'value',
28288 store : new Roo.data.SimpleStore({
28289 data : (function() {
28291 _this.fireEvent('days', _this, days);
28294 fields : [ 'value' ]
28297 select : function (_self, record, index)
28299 _this.setValue(_this.getValue());
28304 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28306 this.monthField = new Roo.bootstrap.MonthField({
28307 after : '<i class=\"fa fa-calendar\"></i>',
28308 allowBlank : this.monthAllowBlank,
28309 placeholder : this.monthPlaceholder,
28312 render : function (_self)
28314 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28315 e.preventDefault();
28319 select : function (_self, oldvalue, newvalue)
28321 _this.setValue(_this.getValue());
28326 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28328 this.yearField = new Roo.bootstrap.ComboBox({
28329 allowBlank : this.yearAllowBlank,
28330 alwaysQuery : true,
28331 displayField : 'value',
28334 forceSelection : true,
28336 placeholder : this.yearPlaceholder,
28337 selectOnFocus : true,
28338 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28339 triggerAction : 'all',
28341 valueField : 'value',
28342 store : new Roo.data.SimpleStore({
28343 data : (function() {
28345 _this.fireEvent('years', _this, years);
28348 fields : [ 'value' ]
28351 select : function (_self, record, index)
28353 _this.setValue(_this.getValue());
28358 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28361 setValue : function(v, format)
28363 this.inputEl.dom.value = v;
28365 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28367 var d = Date.parseDate(v, f);
28374 this.setDay(d.format(this.dayFormat));
28375 this.setMonth(d.format(this.monthFormat));
28376 this.setYear(d.format(this.yearFormat));
28383 setDay : function(v)
28385 this.dayField.setValue(v);
28386 this.inputEl.dom.value = this.getValue();
28391 setMonth : function(v)
28393 this.monthField.setValue(v, true);
28394 this.inputEl.dom.value = this.getValue();
28399 setYear : function(v)
28401 this.yearField.setValue(v);
28402 this.inputEl.dom.value = this.getValue();
28407 getDay : function()
28409 return this.dayField.getValue();
28412 getMonth : function()
28414 return this.monthField.getValue();
28417 getYear : function()
28419 return this.yearField.getValue();
28422 getValue : function()
28424 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28426 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28436 this.inputEl.dom.value = '';
28441 validate : function()
28443 var d = this.dayField.validate();
28444 var m = this.monthField.validate();
28445 var y = this.yearField.validate();
28450 (!this.dayAllowBlank && !d) ||
28451 (!this.monthAllowBlank && !m) ||
28452 (!this.yearAllowBlank && !y)
28457 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28466 this.markInvalid();
28471 markValid : function()
28474 var label = this.el.select('label', true).first();
28475 var icon = this.el.select('i.fa-star', true).first();
28481 this.fireEvent('valid', this);
28485 * Mark this field as invalid
28486 * @param {String} msg The validation message
28488 markInvalid : function(msg)
28491 var label = this.el.select('label', true).first();
28492 var icon = this.el.select('i.fa-star', true).first();
28494 if(label && !icon){
28495 this.el.select('.roo-date-split-field-label', true).createChild({
28497 cls : 'text-danger fa fa-lg fa-star',
28498 tooltip : 'This field is required',
28499 style : 'margin-right:5px;'
28503 this.fireEvent('invalid', this, msg);
28506 clearInvalid : function()
28508 var label = this.el.select('label', true).first();
28509 var icon = this.el.select('i.fa-star', true).first();
28515 this.fireEvent('valid', this);
28518 getName: function()
28528 * http://masonry.desandro.com
28530 * The idea is to render all the bricks based on vertical width...
28532 * The original code extends 'outlayer' - we might need to use that....
28538 * @class Roo.bootstrap.LayoutMasonry
28539 * @extends Roo.bootstrap.Component
28540 * Bootstrap Layout Masonry class
28543 * Create a new Element
28544 * @param {Object} config The config object
28547 Roo.bootstrap.LayoutMasonry = function(config){
28548 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28554 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
28557 * @cfg {Boolean} isLayoutInstant = no animation?
28559 isLayoutInstant : false, // needed?
28562 * @cfg {Number} boxWidth width of the columns
28567 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
28572 * @cfg {Number} padWidth padding below box..
28577 * @cfg {Number} gutter gutter width..
28582 * @cfg {Number} maxCols maximum number of columns
28588 * @cfg {Boolean} isAutoInitial defalut true
28590 isAutoInitial : true,
28595 * @cfg {Boolean} isHorizontal defalut false
28597 isHorizontal : false,
28599 currentSize : null,
28605 bricks: null, //CompositeElement
28609 _isLayoutInited : false,
28611 // isAlternative : false, // only use for vertical layout...
28614 * @cfg {Number} alternativePadWidth padding below box..
28616 alternativePadWidth : 50,
28618 getAutoCreate : function(){
28622 cls: 'blog-masonary-wrapper ' + this.cls,
28624 cls : 'mas-boxes masonary'
28631 getChildContainer: function( )
28633 if (this.boxesEl) {
28634 return this.boxesEl;
28637 this.boxesEl = this.el.select('.mas-boxes').first();
28639 return this.boxesEl;
28643 initEvents : function()
28647 if(this.isAutoInitial){
28648 Roo.log('hook children rendered');
28649 this.on('childrenrendered', function() {
28650 Roo.log('children rendered');
28656 initial : function()
28658 this.currentSize = this.el.getBox(true);
28660 Roo.EventManager.onWindowResize(this.resize, this);
28662 if(!this.isAutoInitial){
28670 //this.layout.defer(500,this);
28674 resize : function()
28678 var cs = this.el.getBox(true);
28680 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28681 Roo.log("no change in with or X");
28685 this.currentSize = cs;
28691 layout : function()
28693 this._resetLayout();
28695 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28697 this.layoutItems( isInstant );
28699 this._isLayoutInited = true;
28703 _resetLayout : function()
28705 if(this.isHorizontal){
28706 this.horizontalMeasureColumns();
28710 this.verticalMeasureColumns();
28714 verticalMeasureColumns : function()
28716 this.getContainerWidth();
28718 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28719 // this.colWidth = Math.floor(this.containerWidth * 0.8);
28723 var boxWidth = this.boxWidth + this.padWidth;
28725 if(this.containerWidth < this.boxWidth){
28726 boxWidth = this.containerWidth
28729 var containerWidth = this.containerWidth;
28731 var cols = Math.floor(containerWidth / boxWidth);
28733 this.cols = Math.max( cols, 1 );
28735 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28737 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28739 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28741 this.colWidth = boxWidth + avail - this.padWidth;
28743 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28744 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
28747 horizontalMeasureColumns : function()
28749 this.getContainerWidth();
28751 var boxWidth = this.boxWidth;
28753 if(this.containerWidth < boxWidth){
28754 boxWidth = this.containerWidth;
28757 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28759 this.el.setHeight(boxWidth);
28763 getContainerWidth : function()
28765 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
28768 layoutItems : function( isInstant )
28770 var items = Roo.apply([], this.bricks);
28772 if(this.isHorizontal){
28773 this._horizontalLayoutItems( items , isInstant );
28777 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28778 // this._verticalAlternativeLayoutItems( items , isInstant );
28782 this._verticalLayoutItems( items , isInstant );
28786 _verticalLayoutItems : function ( items , isInstant)
28788 if ( !items || !items.length ) {
28793 ['xs', 'xs', 'xs', 'tall'],
28794 ['xs', 'xs', 'tall'],
28795 ['xs', 'xs', 'sm'],
28796 ['xs', 'xs', 'xs'],
28802 ['sm', 'xs', 'xs'],
28806 ['tall', 'xs', 'xs', 'xs'],
28807 ['tall', 'xs', 'xs'],
28819 Roo.each(items, function(item, k){
28821 switch (item.size) {
28822 // these layouts take up a full box,
28833 boxes.push([item]);
28856 var filterPattern = function(box, length)
28864 var pattern = box.slice(0, length);
28868 Roo.each(pattern, function(i){
28869 format.push(i.size);
28872 Roo.each(standard, function(s){
28874 if(String(s) != String(format)){
28883 if(!match && length == 1){
28888 filterPattern(box, length - 1);
28892 queue.push(pattern);
28894 box = box.slice(length, box.length);
28896 filterPattern(box, 4);
28902 Roo.each(boxes, function(box, k){
28908 if(box.length == 1){
28913 filterPattern(box, 4);
28917 this._processVerticalLayoutQueue( queue, isInstant );
28921 // _verticalAlternativeLayoutItems : function( items , isInstant )
28923 // if ( !items || !items.length ) {
28927 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
28931 _horizontalLayoutItems : function ( items , isInstant)
28933 if ( !items || !items.length || items.length < 3) {
28939 var eItems = items.slice(0, 3);
28941 items = items.slice(3, items.length);
28944 ['xs', 'xs', 'xs', 'wide'],
28945 ['xs', 'xs', 'wide'],
28946 ['xs', 'xs', 'sm'],
28947 ['xs', 'xs', 'xs'],
28953 ['sm', 'xs', 'xs'],
28957 ['wide', 'xs', 'xs', 'xs'],
28958 ['wide', 'xs', 'xs'],
28971 Roo.each(items, function(item, k){
28973 switch (item.size) {
28984 boxes.push([item]);
29008 var filterPattern = function(box, length)
29016 var pattern = box.slice(0, length);
29020 Roo.each(pattern, function(i){
29021 format.push(i.size);
29024 Roo.each(standard, function(s){
29026 if(String(s) != String(format)){
29035 if(!match && length == 1){
29040 filterPattern(box, length - 1);
29044 queue.push(pattern);
29046 box = box.slice(length, box.length);
29048 filterPattern(box, 4);
29054 Roo.each(boxes, function(box, k){
29060 if(box.length == 1){
29065 filterPattern(box, 4);
29072 var pos = this.el.getBox(true);
29076 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29078 var hit_end = false;
29080 Roo.each(queue, function(box){
29084 Roo.each(box, function(b){
29086 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29096 Roo.each(box, function(b){
29098 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29101 mx = Math.max(mx, b.x);
29105 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29109 Roo.each(box, function(b){
29111 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29125 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29128 /** Sets position of item in DOM
29129 * @param {Element} item
29130 * @param {Number} x - horizontal position
29131 * @param {Number} y - vertical position
29132 * @param {Boolean} isInstant - disables transitions
29134 _processVerticalLayoutQueue : function( queue, isInstant )
29136 var pos = this.el.getBox(true);
29141 for (var i = 0; i < this.cols; i++){
29145 Roo.each(queue, function(box, k){
29147 var col = k % this.cols;
29149 Roo.each(box, function(b,kk){
29151 b.el.position('absolute');
29153 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29154 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29156 if(b.size == 'md-left' || b.size == 'md-right'){
29157 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29158 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29161 b.el.setWidth(width);
29162 b.el.setHeight(height);
29164 b.el.select('iframe',true).setSize(width,height);
29168 for (var i = 0; i < this.cols; i++){
29170 if(maxY[i] < maxY[col]){
29175 col = Math.min(col, i);
29179 x = pos.x + col * (this.colWidth + this.padWidth);
29183 var positions = [];
29185 switch (box.length){
29187 positions = this.getVerticalOneBoxColPositions(x, y, box);
29190 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29193 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29196 positions = this.getVerticalFourBoxColPositions(x, y, box);
29202 Roo.each(box, function(b,kk){
29204 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29206 var sz = b.el.getSize();
29208 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29216 for (var i = 0; i < this.cols; i++){
29217 mY = Math.max(mY, maxY[i]);
29220 this.el.setHeight(mY - pos.y);
29224 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29226 // var pos = this.el.getBox(true);
29229 // var maxX = pos.right;
29231 // var maxHeight = 0;
29233 // Roo.each(items, function(item, k){
29237 // item.el.position('absolute');
29239 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29241 // item.el.setWidth(width);
29243 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29245 // item.el.setHeight(height);
29248 // item.el.setXY([x, y], isInstant ? false : true);
29250 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29253 // y = y + height + this.alternativePadWidth;
29255 // maxHeight = maxHeight + height + this.alternativePadWidth;
29259 // this.el.setHeight(maxHeight);
29263 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29265 var pos = this.el.getBox(true);
29270 var maxX = pos.right;
29272 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29274 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29276 Roo.each(queue, function(box, k){
29278 Roo.each(box, function(b, kk){
29280 b.el.position('absolute');
29282 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29283 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29285 if(b.size == 'md-left' || b.size == 'md-right'){
29286 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29287 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29290 b.el.setWidth(width);
29291 b.el.setHeight(height);
29299 var positions = [];
29301 switch (box.length){
29303 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29306 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29309 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29312 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29318 Roo.each(box, function(b,kk){
29320 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29322 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29330 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29332 Roo.each(eItems, function(b,k){
29334 b.size = (k == 0) ? 'sm' : 'xs';
29335 b.x = (k == 0) ? 2 : 1;
29336 b.y = (k == 0) ? 2 : 1;
29338 b.el.position('absolute');
29340 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29342 b.el.setWidth(width);
29344 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29346 b.el.setHeight(height);
29350 var positions = [];
29353 x : maxX - this.unitWidth * 2 - this.gutter,
29358 x : maxX - this.unitWidth,
29359 y : minY + (this.unitWidth + this.gutter) * 2
29363 x : maxX - this.unitWidth * 3 - this.gutter * 2,
29367 Roo.each(eItems, function(b,k){
29369 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29375 getVerticalOneBoxColPositions : function(x, y, box)
29379 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29381 if(box[0].size == 'md-left'){
29385 if(box[0].size == 'md-right'){
29390 x : x + (this.unitWidth + this.gutter) * rand,
29397 getVerticalTwoBoxColPositions : function(x, y, box)
29401 if(box[0].size == 'xs'){
29405 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29409 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29423 x : x + (this.unitWidth + this.gutter) * 2,
29424 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29431 getVerticalThreeBoxColPositions : function(x, y, box)
29435 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29443 x : x + (this.unitWidth + this.gutter) * 1,
29448 x : x + (this.unitWidth + this.gutter) * 2,
29456 if(box[0].size == 'xs' && box[1].size == 'xs'){
29465 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29469 x : x + (this.unitWidth + this.gutter) * 1,
29483 x : x + (this.unitWidth + this.gutter) * 2,
29488 x : x + (this.unitWidth + this.gutter) * 2,
29489 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29496 getVerticalFourBoxColPositions : function(x, y, box)
29500 if(box[0].size == 'xs'){
29509 y : y + (this.unitHeight + this.gutter) * 1
29514 y : y + (this.unitHeight + this.gutter) * 2
29518 x : x + (this.unitWidth + this.gutter) * 1,
29532 x : x + (this.unitWidth + this.gutter) * 2,
29537 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29538 y : y + (this.unitHeight + this.gutter) * 1
29542 x : x + (this.unitWidth + this.gutter) * 2,
29543 y : y + (this.unitWidth + this.gutter) * 2
29550 getHorizontalOneBoxColPositions : function(maxX, minY, box)
29554 if(box[0].size == 'md-left'){
29556 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29563 if(box[0].size == 'md-right'){
29565 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29566 y : minY + (this.unitWidth + this.gutter) * 1
29572 var rand = Math.floor(Math.random() * (4 - box[0].y));
29575 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29576 y : minY + (this.unitWidth + this.gutter) * rand
29583 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29587 if(box[0].size == 'xs'){
29590 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29595 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29596 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29604 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29609 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29610 y : minY + (this.unitWidth + this.gutter) * 2
29617 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29621 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29624 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29629 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29630 y : minY + (this.unitWidth + this.gutter) * 1
29634 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29635 y : minY + (this.unitWidth + this.gutter) * 2
29642 if(box[0].size == 'xs' && box[1].size == 'xs'){
29645 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29650 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29655 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29656 y : minY + (this.unitWidth + this.gutter) * 1
29664 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29669 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29670 y : minY + (this.unitWidth + this.gutter) * 2
29674 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29675 y : minY + (this.unitWidth + this.gutter) * 2
29682 getHorizontalFourBoxColPositions : function(maxX, minY, box)
29686 if(box[0].size == 'xs'){
29689 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29694 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29699 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),
29704 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29705 y : minY + (this.unitWidth + this.gutter) * 1
29713 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29718 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29719 y : minY + (this.unitWidth + this.gutter) * 2
29723 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29724 y : minY + (this.unitWidth + this.gutter) * 2
29728 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),
29729 y : minY + (this.unitWidth + this.gutter) * 2
29743 * http://masonry.desandro.com
29745 * The idea is to render all the bricks based on vertical width...
29747 * The original code extends 'outlayer' - we might need to use that....
29753 * @class Roo.bootstrap.LayoutMasonryAuto
29754 * @extends Roo.bootstrap.Component
29755 * Bootstrap Layout Masonry class
29758 * Create a new Element
29759 * @param {Object} config The config object
29762 Roo.bootstrap.LayoutMasonryAuto = function(config){
29763 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29766 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
29769 * @cfg {Boolean} isFitWidth - resize the width..
29771 isFitWidth : false, // options..
29773 * @cfg {Boolean} isOriginLeft = left align?
29775 isOriginLeft : true,
29777 * @cfg {Boolean} isOriginTop = top align?
29779 isOriginTop : false,
29781 * @cfg {Boolean} isLayoutInstant = no animation?
29783 isLayoutInstant : false, // needed?
29785 * @cfg {Boolean} isResizingContainer = not sure if this is used..
29787 isResizingContainer : true,
29789 * @cfg {Number} columnWidth width of the columns
29795 * @cfg {Number} maxCols maximum number of columns
29800 * @cfg {Number} padHeight padding below box..
29806 * @cfg {Boolean} isAutoInitial defalut true
29809 isAutoInitial : true,
29815 initialColumnWidth : 0,
29816 currentSize : null,
29818 colYs : null, // array.
29825 bricks: null, //CompositeElement
29826 cols : 0, // array?
29827 // element : null, // wrapped now this.el
29828 _isLayoutInited : null,
29831 getAutoCreate : function(){
29835 cls: 'blog-masonary-wrapper ' + this.cls,
29837 cls : 'mas-boxes masonary'
29844 getChildContainer: function( )
29846 if (this.boxesEl) {
29847 return this.boxesEl;
29850 this.boxesEl = this.el.select('.mas-boxes').first();
29852 return this.boxesEl;
29856 initEvents : function()
29860 if(this.isAutoInitial){
29861 Roo.log('hook children rendered');
29862 this.on('childrenrendered', function() {
29863 Roo.log('children rendered');
29870 initial : function()
29872 this.reloadItems();
29874 this.currentSize = this.el.getBox(true);
29876 /// was window resize... - let's see if this works..
29877 Roo.EventManager.onWindowResize(this.resize, this);
29879 if(!this.isAutoInitial){
29884 this.layout.defer(500,this);
29887 reloadItems: function()
29889 this.bricks = this.el.select('.masonry-brick', true);
29891 this.bricks.each(function(b) {
29892 //Roo.log(b.getSize());
29893 if (!b.attr('originalwidth')) {
29894 b.attr('originalwidth', b.getSize().width);
29899 Roo.log(this.bricks.elements.length);
29902 resize : function()
29905 var cs = this.el.getBox(true);
29907 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29908 Roo.log("no change in with or X");
29911 this.currentSize = cs;
29915 layout : function()
29918 this._resetLayout();
29919 //this._manageStamps();
29921 // don't animate first layout
29922 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29923 this.layoutItems( isInstant );
29925 // flag for initalized
29926 this._isLayoutInited = true;
29929 layoutItems : function( isInstant )
29931 //var items = this._getItemsForLayout( this.items );
29932 // original code supports filtering layout items.. we just ignore it..
29934 this._layoutItems( this.bricks , isInstant );
29936 this._postLayout();
29938 _layoutItems : function ( items , isInstant)
29940 //this.fireEvent( 'layout', this, items );
29943 if ( !items || !items.elements.length ) {
29944 // no items, emit event with empty array
29949 items.each(function(item) {
29950 Roo.log("layout item");
29952 // get x/y object from method
29953 var position = this._getItemLayoutPosition( item );
29955 position.item = item;
29956 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29957 queue.push( position );
29960 this._processLayoutQueue( queue );
29962 /** Sets position of item in DOM
29963 * @param {Element} item
29964 * @param {Number} x - horizontal position
29965 * @param {Number} y - vertical position
29966 * @param {Boolean} isInstant - disables transitions
29968 _processLayoutQueue : function( queue )
29970 for ( var i=0, len = queue.length; i < len; i++ ) {
29971 var obj = queue[i];
29972 obj.item.position('absolute');
29973 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
29979 * Any logic you want to do after each layout,
29980 * i.e. size the container
29982 _postLayout : function()
29984 this.resizeContainer();
29987 resizeContainer : function()
29989 if ( !this.isResizingContainer ) {
29992 var size = this._getContainerSize();
29994 this.el.setSize(size.width,size.height);
29995 this.boxesEl.setSize(size.width,size.height);
30001 _resetLayout : function()
30003 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30004 this.colWidth = this.el.getWidth();
30005 //this.gutter = this.el.getWidth();
30007 this.measureColumns();
30013 this.colYs.push( 0 );
30019 measureColumns : function()
30021 this.getContainerWidth();
30022 // if columnWidth is 0, default to outerWidth of first item
30023 if ( !this.columnWidth ) {
30024 var firstItem = this.bricks.first();
30025 Roo.log(firstItem);
30026 this.columnWidth = this.containerWidth;
30027 if (firstItem && firstItem.attr('originalwidth') ) {
30028 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30030 // columnWidth fall back to item of first element
30031 Roo.log("set column width?");
30032 this.initialColumnWidth = this.columnWidth ;
30034 // if first elem has no width, default to size of container
30039 if (this.initialColumnWidth) {
30040 this.columnWidth = this.initialColumnWidth;
30045 // column width is fixed at the top - however if container width get's smaller we should
30048 // this bit calcs how man columns..
30050 var columnWidth = this.columnWidth += this.gutter;
30052 // calculate columns
30053 var containerWidth = this.containerWidth + this.gutter;
30055 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30056 // fix rounding errors, typically with gutters
30057 var excess = columnWidth - containerWidth % columnWidth;
30060 // if overshoot is less than a pixel, round up, otherwise floor it
30061 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30062 cols = Math[ mathMethod ]( cols );
30063 this.cols = Math.max( cols, 1 );
30064 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30066 // padding positioning..
30067 var totalColWidth = this.cols * this.columnWidth;
30068 var padavail = this.containerWidth - totalColWidth;
30069 // so for 2 columns - we need 3 'pads'
30071 var padNeeded = (1+this.cols) * this.padWidth;
30073 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30075 this.columnWidth += padExtra
30076 //this.padWidth = Math.floor(padavail / ( this.cols));
30078 // adjust colum width so that padding is fixed??
30080 // we have 3 columns ... total = width * 3
30081 // we have X left over... that should be used by
30083 //if (this.expandC) {
30091 getContainerWidth : function()
30093 /* // container is parent if fit width
30094 var container = this.isFitWidth ? this.element.parentNode : this.element;
30095 // check that this.size and size are there
30096 // IE8 triggers resize on body size change, so they might not be
30098 var size = getSize( container ); //FIXME
30099 this.containerWidth = size && size.innerWidth; //FIXME
30102 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30106 _getItemLayoutPosition : function( item ) // what is item?
30108 // we resize the item to our columnWidth..
30110 item.setWidth(this.columnWidth);
30111 item.autoBoxAdjust = false;
30113 var sz = item.getSize();
30115 // how many columns does this brick span
30116 var remainder = this.containerWidth % this.columnWidth;
30118 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30119 // round if off by 1 pixel, otherwise use ceil
30120 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30121 colSpan = Math.min( colSpan, this.cols );
30123 // normally this should be '1' as we dont' currently allow multi width columns..
30125 var colGroup = this._getColGroup( colSpan );
30126 // get the minimum Y value from the columns
30127 var minimumY = Math.min.apply( Math, colGroup );
30128 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30130 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30132 // position the brick
30134 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30135 y: this.currentSize.y + minimumY + this.padHeight
30139 // apply setHeight to necessary columns
30140 var setHeight = minimumY + sz.height + this.padHeight;
30141 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30143 var setSpan = this.cols + 1 - colGroup.length;
30144 for ( var i = 0; i < setSpan; i++ ) {
30145 this.colYs[ shortColIndex + i ] = setHeight ;
30152 * @param {Number} colSpan - number of columns the element spans
30153 * @returns {Array} colGroup
30155 _getColGroup : function( colSpan )
30157 if ( colSpan < 2 ) {
30158 // if brick spans only one column, use all the column Ys
30163 // how many different places could this brick fit horizontally
30164 var groupCount = this.cols + 1 - colSpan;
30165 // for each group potential horizontal position
30166 for ( var i = 0; i < groupCount; i++ ) {
30167 // make an array of colY values for that one group
30168 var groupColYs = this.colYs.slice( i, i + colSpan );
30169 // and get the max value of the array
30170 colGroup[i] = Math.max.apply( Math, groupColYs );
30175 _manageStamp : function( stamp )
30177 var stampSize = stamp.getSize();
30178 var offset = stamp.getBox();
30179 // get the columns that this stamp affects
30180 var firstX = this.isOriginLeft ? offset.x : offset.right;
30181 var lastX = firstX + stampSize.width;
30182 var firstCol = Math.floor( firstX / this.columnWidth );
30183 firstCol = Math.max( 0, firstCol );
30185 var lastCol = Math.floor( lastX / this.columnWidth );
30186 // lastCol should not go over if multiple of columnWidth #425
30187 lastCol -= lastX % this.columnWidth ? 0 : 1;
30188 lastCol = Math.min( this.cols - 1, lastCol );
30190 // set colYs to bottom of the stamp
30191 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30194 for ( var i = firstCol; i <= lastCol; i++ ) {
30195 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30200 _getContainerSize : function()
30202 this.maxY = Math.max.apply( Math, this.colYs );
30207 if ( this.isFitWidth ) {
30208 size.width = this._getContainerFitWidth();
30214 _getContainerFitWidth : function()
30216 var unusedCols = 0;
30217 // count unused columns
30220 if ( this.colYs[i] !== 0 ) {
30225 // fit container to columns that have been used
30226 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30229 needsResizeLayout : function()
30231 var previousWidth = this.containerWidth;
30232 this.getContainerWidth();
30233 return previousWidth !== this.containerWidth;
30248 * @class Roo.bootstrap.MasonryBrick
30249 * @extends Roo.bootstrap.Component
30250 * Bootstrap MasonryBrick class
30253 * Create a new MasonryBrick
30254 * @param {Object} config The config object
30257 Roo.bootstrap.MasonryBrick = function(config){
30258 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30264 * When a MasonryBrick is clcik
30265 * @param {Roo.bootstrap.MasonryBrick} this
30266 * @param {Roo.EventObject} e
30272 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30275 * @cfg {String} title
30279 * @cfg {String} html
30283 * @cfg {String} bgimage
30287 * @cfg {String} videourl
30291 * @cfg {String} cls
30295 * @cfg {String} href
30299 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30304 * @cfg {String} (center|bottom) placetitle
30308 getAutoCreate : function()
30310 var cls = 'masonry-brick';
30312 if(this.href.length){
30313 cls += ' masonry-brick-link';
30316 if(this.bgimage.length){
30317 cls += ' masonry-brick-image';
30321 cls += ' masonry-' + this.size + '-brick';
30324 if(this.placetitle.length){
30326 switch (this.placetitle) {
30328 cls += ' masonry-center-title';
30331 cls += ' masonry-bottom-title';
30338 if(!this.html.length && !this.bgimage.length){
30339 cls += ' masonry-center-title';
30342 if(!this.html.length && this.bgimage.length){
30343 cls += ' masonry-bottom-title';
30348 cls += ' ' + this.cls;
30352 tag: (this.href.length) ? 'a' : 'div',
30357 cls: 'masonry-brick-paragraph',
30363 if(this.href.length){
30364 cfg.href = this.href;
30367 var cn = cfg.cn[0].cn;
30369 if(this.title.length){
30372 cls: 'masonry-brick-title',
30377 if(this.html.length){
30380 cls: 'masonry-brick-text',
30384 if (!this.title.length && !this.html.length) {
30385 cfg.cn[0].cls += ' hide';
30388 if(this.bgimage.length){
30391 cls: 'masonry-brick-image-view',
30395 if(this.videourl.length){
30396 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30397 // youtube support only?
30400 cls: 'masonry-brick-image-view',
30403 allowfullscreen : true
30412 initEvents: function()
30414 switch (this.size) {
30416 // this.intSize = 1;
30421 // this.intSize = 2;
30428 // this.intSize = 3;
30433 // this.intSize = 3;
30438 // this.intSize = 3;
30443 // this.intSize = 3;
30455 this.el.on('touchstart', this.onTouchStart, this);
30456 this.el.on('touchmove', this.onTouchMove, this);
30457 this.el.on('touchend', this.onTouchEnd, this);
30458 this.el.on('contextmenu', this.onContextMenu, this);
30460 this.el.on('mouseenter' ,this.enter, this);
30461 this.el.on('mouseleave', this.leave, this);
30464 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30465 this.parent().bricks.push(this);
30470 onClick: function(e, el)
30476 var time = this.endTimer - this.startTimer;
30484 e.preventDefault();
30487 enter: function(e, el)
30489 e.preventDefault();
30491 if(this.bgimage.length && this.html.length){
30492 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30496 leave: function(e, el)
30498 e.preventDefault();
30500 if(this.bgimage.length && this.html.length){
30501 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30505 onTouchStart: function(e, el)
30507 // e.preventDefault();
30509 this.touchmoved = false;
30511 if(!this.bgimage.length || !this.html.length){
30515 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30517 this.timer = new Date().getTime();
30521 onTouchMove: function(e, el)
30523 this.touchmoved = true;
30526 onContextMenu : function(e,el)
30528 e.preventDefault();
30529 e.stopPropagation();
30533 onTouchEnd: function(e, el)
30535 // e.preventDefault();
30537 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30544 if(!this.bgimage.length || !this.html.length){
30546 if(this.href.length){
30547 window.location.href = this.href;
30553 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30555 window.location.href = this.href;
30570 * @class Roo.bootstrap.Brick
30571 * @extends Roo.bootstrap.Component
30572 * Bootstrap Brick class
30575 * Create a new Brick
30576 * @param {Object} config The config object
30579 Roo.bootstrap.Brick = function(config){
30580 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30586 * When a Brick is click
30587 * @param {Roo.bootstrap.Brick} this
30588 * @param {Roo.EventObject} e
30594 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
30597 * @cfg {String} title
30601 * @cfg {String} html
30605 * @cfg {String} bgimage
30609 * @cfg {String} cls
30613 * @cfg {String} href
30617 * @cfg {String} video
30621 * @cfg {Boolean} square
30625 getAutoCreate : function()
30627 var cls = 'roo-brick';
30629 if(this.href.length){
30630 cls += ' roo-brick-link';
30633 if(this.bgimage.length){
30634 cls += ' roo-brick-image';
30637 if(!this.html.length && !this.bgimage.length){
30638 cls += ' roo-brick-center-title';
30641 if(!this.html.length && this.bgimage.length){
30642 cls += ' roo-brick-bottom-title';
30646 cls += ' ' + this.cls;
30650 tag: (this.href.length) ? 'a' : 'div',
30655 cls: 'roo-brick-paragraph',
30661 if(this.href.length){
30662 cfg.href = this.href;
30665 var cn = cfg.cn[0].cn;
30667 if(this.title.length){
30670 cls: 'roo-brick-title',
30675 if(this.html.length){
30678 cls: 'roo-brick-text',
30685 if(this.bgimage.length){
30688 cls: 'roo-brick-image-view',
30696 initEvents: function()
30698 if(this.title.length || this.html.length){
30699 this.el.on('mouseenter' ,this.enter, this);
30700 this.el.on('mouseleave', this.leave, this);
30704 Roo.EventManager.onWindowResize(this.resize, this);
30709 resize : function()
30711 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30713 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30714 // paragraph.setHeight(paragraph.getWidth());
30716 if(this.bgimage.length){
30717 var image = this.el.select('.roo-brick-image-view', true).first();
30718 image.setWidth(paragraph.getWidth());
30719 image.setHeight(paragraph.getWidth());
30724 enter: function(e, el)
30726 e.preventDefault();
30728 if(this.bgimage.length){
30729 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30730 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30734 leave: function(e, el)
30736 e.preventDefault();
30738 if(this.bgimage.length){
30739 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30740 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30750 * Ext JS Library 1.1.1
30751 * Copyright(c) 2006-2007, Ext JS, LLC.
30753 * Originally Released Under LGPL - original licence link has changed is not relivant.
30756 * <script type="text/javascript">
30761 * @class Roo.bootstrap.SplitBar
30762 * @extends Roo.util.Observable
30763 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30767 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30768 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30769 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30770 split.minSize = 100;
30771 split.maxSize = 600;
30772 split.animate = true;
30773 split.on('moved', splitterMoved);
30776 * Create a new SplitBar
30777 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
30778 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
30779 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30780 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
30781 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30782 position of the SplitBar).
30784 Roo.bootstrap.SplitBar = function(cfg){
30789 // dragElement : elm
30790 // resizingElement: el,
30792 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30793 // placement : Roo.bootstrap.SplitBar.LEFT ,
30794 // existingProxy ???
30797 this.el = Roo.get(cfg.dragElement, true);
30798 this.el.dom.unselectable = "on";
30800 this.resizingEl = Roo.get(cfg.resizingElement, true);
30804 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30805 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30808 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30811 * The minimum size of the resizing element. (Defaults to 0)
30817 * The maximum size of the resizing element. (Defaults to 2000)
30820 this.maxSize = 2000;
30823 * Whether to animate the transition to the new size
30826 this.animate = false;
30829 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30832 this.useShim = false;
30837 if(!cfg.existingProxy){
30839 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30841 this.proxy = Roo.get(cfg.existingProxy).dom;
30844 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30847 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30850 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30853 this.dragSpecs = {};
30856 * @private The adapter to use to positon and resize elements
30858 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30859 this.adapter.init(this);
30861 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30863 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30864 this.el.addClass("roo-splitbar-h");
30867 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30868 this.el.addClass("roo-splitbar-v");
30874 * Fires when the splitter is moved (alias for {@link #event-moved})
30875 * @param {Roo.bootstrap.SplitBar} this
30876 * @param {Number} newSize the new width or height
30881 * Fires when the splitter is moved
30882 * @param {Roo.bootstrap.SplitBar} this
30883 * @param {Number} newSize the new width or height
30887 * @event beforeresize
30888 * Fires before the splitter is dragged
30889 * @param {Roo.bootstrap.SplitBar} this
30891 "beforeresize" : true,
30893 "beforeapply" : true
30896 Roo.util.Observable.call(this);
30899 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30900 onStartProxyDrag : function(x, y){
30901 this.fireEvent("beforeresize", this);
30903 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
30905 o.enableDisplayMode("block");
30906 // all splitbars share the same overlay
30907 Roo.bootstrap.SplitBar.prototype.overlay = o;
30909 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30910 this.overlay.show();
30911 Roo.get(this.proxy).setDisplayed("block");
30912 var size = this.adapter.getElementSize(this);
30913 this.activeMinSize = this.getMinimumSize();;
30914 this.activeMaxSize = this.getMaximumSize();;
30915 var c1 = size - this.activeMinSize;
30916 var c2 = Math.max(this.activeMaxSize - size, 0);
30917 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30918 this.dd.resetConstraints();
30919 this.dd.setXConstraint(
30920 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
30921 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30923 this.dd.setYConstraint(0, 0);
30925 this.dd.resetConstraints();
30926 this.dd.setXConstraint(0, 0);
30927 this.dd.setYConstraint(
30928 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
30929 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30932 this.dragSpecs.startSize = size;
30933 this.dragSpecs.startPoint = [x, y];
30934 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30938 * @private Called after the drag operation by the DDProxy
30940 onEndProxyDrag : function(e){
30941 Roo.get(this.proxy).setDisplayed(false);
30942 var endPoint = Roo.lib.Event.getXY(e);
30944 this.overlay.hide();
30947 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30948 newSize = this.dragSpecs.startSize +
30949 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30950 endPoint[0] - this.dragSpecs.startPoint[0] :
30951 this.dragSpecs.startPoint[0] - endPoint[0]
30954 newSize = this.dragSpecs.startSize +
30955 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30956 endPoint[1] - this.dragSpecs.startPoint[1] :
30957 this.dragSpecs.startPoint[1] - endPoint[1]
30960 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30961 if(newSize != this.dragSpecs.startSize){
30962 if(this.fireEvent('beforeapply', this, newSize) !== false){
30963 this.adapter.setElementSize(this, newSize);
30964 this.fireEvent("moved", this, newSize);
30965 this.fireEvent("resize", this, newSize);
30971 * Get the adapter this SplitBar uses
30972 * @return The adapter object
30974 getAdapter : function(){
30975 return this.adapter;
30979 * Set the adapter this SplitBar uses
30980 * @param {Object} adapter A SplitBar adapter object
30982 setAdapter : function(adapter){
30983 this.adapter = adapter;
30984 this.adapter.init(this);
30988 * Gets the minimum size for the resizing element
30989 * @return {Number} The minimum size
30991 getMinimumSize : function(){
30992 return this.minSize;
30996 * Sets the minimum size for the resizing element
30997 * @param {Number} minSize The minimum size
30999 setMinimumSize : function(minSize){
31000 this.minSize = minSize;
31004 * Gets the maximum size for the resizing element
31005 * @return {Number} The maximum size
31007 getMaximumSize : function(){
31008 return this.maxSize;
31012 * Sets the maximum size for the resizing element
31013 * @param {Number} maxSize The maximum size
31015 setMaximumSize : function(maxSize){
31016 this.maxSize = maxSize;
31020 * Sets the initialize size for the resizing element
31021 * @param {Number} size The initial size
31023 setCurrentSize : function(size){
31024 var oldAnimate = this.animate;
31025 this.animate = false;
31026 this.adapter.setElementSize(this, size);
31027 this.animate = oldAnimate;
31031 * Destroy this splitbar.
31032 * @param {Boolean} removeEl True to remove the element
31034 destroy : function(removeEl){
31036 this.shim.remove();
31039 this.proxy.parentNode.removeChild(this.proxy);
31047 * @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.
31049 Roo.bootstrap.SplitBar.createProxy = function(dir){
31050 var proxy = new Roo.Element(document.createElement("div"));
31051 proxy.unselectable();
31052 var cls = 'roo-splitbar-proxy';
31053 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31054 document.body.appendChild(proxy.dom);
31059 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31060 * Default Adapter. It assumes the splitter and resizing element are not positioned
31061 * elements and only gets/sets the width of the element. Generally used for table based layouts.
31063 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31066 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31067 // do nothing for now
31068 init : function(s){
31072 * Called before drag operations to get the current size of the resizing element.
31073 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31075 getElementSize : function(s){
31076 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31077 return s.resizingEl.getWidth();
31079 return s.resizingEl.getHeight();
31084 * Called after drag operations to set the size of the resizing element.
31085 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31086 * @param {Number} newSize The new size to set
31087 * @param {Function} onComplete A function to be invoked when resizing is complete
31089 setElementSize : function(s, newSize, onComplete){
31090 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31092 s.resizingEl.setWidth(newSize);
31094 onComplete(s, newSize);
31097 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31102 s.resizingEl.setHeight(newSize);
31104 onComplete(s, newSize);
31107 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31114 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31115 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31116 * Adapter that moves the splitter element to align with the resized sizing element.
31117 * Used with an absolute positioned SplitBar.
31118 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31119 * document.body, make sure you assign an id to the body element.
31121 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31122 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31123 this.container = Roo.get(container);
31126 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31127 init : function(s){
31128 this.basic.init(s);
31131 getElementSize : function(s){
31132 return this.basic.getElementSize(s);
31135 setElementSize : function(s, newSize, onComplete){
31136 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31139 moveSplitter : function(s){
31140 var yes = Roo.bootstrap.SplitBar;
31141 switch(s.placement){
31143 s.el.setX(s.resizingEl.getRight());
31146 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31149 s.el.setY(s.resizingEl.getBottom());
31152 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31159 * Orientation constant - Create a vertical SplitBar
31163 Roo.bootstrap.SplitBar.VERTICAL = 1;
31166 * Orientation constant - Create a horizontal SplitBar
31170 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31173 * Placement constant - The resizing element is to the left of the splitter element
31177 Roo.bootstrap.SplitBar.LEFT = 1;
31180 * Placement constant - The resizing element is to the right of the splitter element
31184 Roo.bootstrap.SplitBar.RIGHT = 2;
31187 * Placement constant - The resizing element is positioned above the splitter element
31191 Roo.bootstrap.SplitBar.TOP = 3;
31194 * Placement constant - The resizing element is positioned under splitter element
31198 Roo.bootstrap.SplitBar.BOTTOM = 4;
31199 Roo.namespace("Roo.bootstrap.layout");/*
31201 * Ext JS Library 1.1.1
31202 * Copyright(c) 2006-2007, Ext JS, LLC.
31204 * Originally Released Under LGPL - original licence link has changed is not relivant.
31207 * <script type="text/javascript">
31211 * @class Roo.bootstrap.layout.Manager
31212 * @extends Roo.bootstrap.Component
31213 * Base class for layout managers.
31215 Roo.bootstrap.layout.Manager = function(config)
31217 Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31223 /** false to disable window resize monitoring @type Boolean */
31224 this.monitorWindowResize = true;
31229 * Fires when a layout is performed.
31230 * @param {Roo.LayoutManager} this
31234 * @event regionresized
31235 * Fires when the user resizes a region.
31236 * @param {Roo.LayoutRegion} region The resized region
31237 * @param {Number} newSize The new size (width for east/west, height for north/south)
31239 "regionresized" : true,
31241 * @event regioncollapsed
31242 * Fires when a region is collapsed.
31243 * @param {Roo.LayoutRegion} region The collapsed region
31245 "regioncollapsed" : true,
31247 * @event regionexpanded
31248 * Fires when a region is expanded.
31249 * @param {Roo.LayoutRegion} region The expanded region
31251 "regionexpanded" : true
31253 this.updating = false;
31256 this.el = Roo.get(config.el);
31262 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31267 monitorWindowResize : true,
31273 onRender : function(ct, position)
31276 this.el = Roo.get(ct);
31282 initEvents: function()
31286 // ie scrollbar fix
31287 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31288 document.body.scroll = "no";
31289 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31290 this.el.position('relative');
31292 this.id = this.el.id;
31293 this.el.addClass("roo-layout-container");
31294 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31295 if(this.el.dom != document.body ) {
31296 this.el.on('resize', this.layout,this);
31297 this.el.on('show', this.layout,this);
31303 * Returns true if this layout is currently being updated
31304 * @return {Boolean}
31306 isUpdating : function(){
31307 return this.updating;
31311 * Suspend the LayoutManager from doing auto-layouts while
31312 * making multiple add or remove calls
31314 beginUpdate : function(){
31315 this.updating = true;
31319 * Restore auto-layouts and optionally disable the manager from performing a layout
31320 * @param {Boolean} noLayout true to disable a layout update
31322 endUpdate : function(noLayout){
31323 this.updating = false;
31329 layout: function(){
31333 onRegionResized : function(region, newSize){
31334 this.fireEvent("regionresized", region, newSize);
31338 onRegionCollapsed : function(region){
31339 this.fireEvent("regioncollapsed", region);
31342 onRegionExpanded : function(region){
31343 this.fireEvent("regionexpanded", region);
31347 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31348 * performs box-model adjustments.
31349 * @return {Object} The size as an object {width: (the width), height: (the height)}
31351 getViewSize : function()
31354 if(this.el.dom != document.body){
31355 size = this.el.getSize();
31357 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31359 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31360 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31365 * Returns the Element this layout is bound to.
31366 * @return {Roo.Element}
31368 getEl : function(){
31373 * Returns the specified region.
31374 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31375 * @return {Roo.LayoutRegion}
31377 getRegion : function(target){
31378 return this.regions[target.toLowerCase()];
31381 onWindowResize : function(){
31382 if(this.monitorWindowResize){
31388 * Ext JS Library 1.1.1
31389 * Copyright(c) 2006-2007, Ext JS, LLC.
31391 * Originally Released Under LGPL - original licence link has changed is not relivant.
31394 * <script type="text/javascript">
31397 * @class Roo.bootstrap.layout.Border
31398 * @extends Roo.bootstrap.layout.Manager
31399 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31400 * please see: examples/bootstrap/nested.html<br><br>
31402 <b>The container the layout is rendered into can be either the body element or any other element.
31403 If it is not the body element, the container needs to either be an absolute positioned element,
31404 or you will need to add "position:relative" to the css of the container. You will also need to specify
31405 the container size if it is not the body element.</b>
31408 * Create a new Border
31409 * @param {Object} config Configuration options
31411 Roo.bootstrap.layout.Border = function(config){
31412 config = config || {};
31413 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31417 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31418 if(config[region]){
31419 config[region].region = region;
31420 this.addRegion(config[region]);
31426 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
31428 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31430 * Creates and adds a new region if it doesn't already exist.
31431 * @param {String} target The target region key (north, south, east, west or center).
31432 * @param {Object} config The regions config object
31433 * @return {BorderLayoutRegion} The new region
31435 addRegion : function(config)
31437 if(!this.regions[config.region]){
31438 var r = this.factory(config);
31439 this.bindRegion(r);
31441 return this.regions[config.region];
31445 bindRegion : function(r){
31446 this.regions[r.config.region] = r;
31448 r.on("visibilitychange", this.layout, this);
31449 r.on("paneladded", this.layout, this);
31450 r.on("panelremoved", this.layout, this);
31451 r.on("invalidated", this.layout, this);
31452 r.on("resized", this.onRegionResized, this);
31453 r.on("collapsed", this.onRegionCollapsed, this);
31454 r.on("expanded", this.onRegionExpanded, this);
31458 * Performs a layout update.
31460 layout : function()
31462 if(this.updating) {
31465 var size = this.getViewSize();
31466 var w = size.width;
31467 var h = size.height;
31472 //var x = 0, y = 0;
31474 var rs = this.regions;
31475 var north = rs["north"];
31476 var south = rs["south"];
31477 var west = rs["west"];
31478 var east = rs["east"];
31479 var center = rs["center"];
31480 //if(this.hideOnLayout){ // not supported anymore
31481 //c.el.setStyle("display", "none");
31483 if(north && north.isVisible()){
31484 var b = north.getBox();
31485 var m = north.getMargins();
31486 b.width = w - (m.left+m.right);
31489 centerY = b.height + b.y + m.bottom;
31490 centerH -= centerY;
31491 north.updateBox(this.safeBox(b));
31493 if(south && south.isVisible()){
31494 var b = south.getBox();
31495 var m = south.getMargins();
31496 b.width = w - (m.left+m.right);
31498 var totalHeight = (b.height + m.top + m.bottom);
31499 b.y = h - totalHeight + m.top;
31500 centerH -= totalHeight;
31501 south.updateBox(this.safeBox(b));
31503 if(west && west.isVisible()){
31504 var b = west.getBox();
31505 var m = west.getMargins();
31506 b.height = centerH - (m.top+m.bottom);
31508 b.y = centerY + m.top;
31509 var totalWidth = (b.width + m.left + m.right);
31510 centerX += totalWidth;
31511 centerW -= totalWidth;
31512 west.updateBox(this.safeBox(b));
31514 if(east && east.isVisible()){
31515 var b = east.getBox();
31516 var m = east.getMargins();
31517 b.height = centerH - (m.top+m.bottom);
31518 var totalWidth = (b.width + m.left + m.right);
31519 b.x = w - totalWidth + m.left;
31520 b.y = centerY + m.top;
31521 centerW -= totalWidth;
31522 east.updateBox(this.safeBox(b));
31525 var m = center.getMargins();
31527 x: centerX + m.left,
31528 y: centerY + m.top,
31529 width: centerW - (m.left+m.right),
31530 height: centerH - (m.top+m.bottom)
31532 //if(this.hideOnLayout){
31533 //center.el.setStyle("display", "block");
31535 center.updateBox(this.safeBox(centerBox));
31538 this.fireEvent("layout", this);
31542 safeBox : function(box){
31543 box.width = Math.max(0, box.width);
31544 box.height = Math.max(0, box.height);
31549 * Adds a ContentPanel (or subclass) to this layout.
31550 * @param {String} target The target region key (north, south, east, west or center).
31551 * @param {Roo.ContentPanel} panel The panel to add
31552 * @return {Roo.ContentPanel} The added panel
31554 add : function(target, panel){
31556 target = target.toLowerCase();
31557 return this.regions[target].add(panel);
31561 * Remove a ContentPanel (or subclass) to this layout.
31562 * @param {String} target The target region key (north, south, east, west or center).
31563 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31564 * @return {Roo.ContentPanel} The removed panel
31566 remove : function(target, panel){
31567 target = target.toLowerCase();
31568 return this.regions[target].remove(panel);
31572 * Searches all regions for a panel with the specified id
31573 * @param {String} panelId
31574 * @return {Roo.ContentPanel} The panel or null if it wasn't found
31576 findPanel : function(panelId){
31577 var rs = this.regions;
31578 for(var target in rs){
31579 if(typeof rs[target] != "function"){
31580 var p = rs[target].getPanel(panelId);
31590 * Searches all regions for a panel with the specified id and activates (shows) it.
31591 * @param {String/ContentPanel} panelId The panels id or the panel itself
31592 * @return {Roo.ContentPanel} The shown panel or null
31594 showPanel : function(panelId) {
31595 var rs = this.regions;
31596 for(var target in rs){
31597 var r = rs[target];
31598 if(typeof r != "function"){
31599 if(r.hasPanel(panelId)){
31600 return r.showPanel(panelId);
31608 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31609 * @param {Roo.state.Provider} provider (optional) An alternate state provider
31612 restoreState : function(provider){
31614 provider = Roo.state.Manager;
31616 var sm = new Roo.LayoutStateManager();
31617 sm.init(this, provider);
31623 * Adds a xtype elements to the layout.
31627 xtype : 'ContentPanel',
31634 xtype : 'NestedLayoutPanel',
31640 items : [ ... list of content panels or nested layout panels.. ]
31644 * @param {Object} cfg Xtype definition of item to add.
31646 addxtype : function(cfg)
31648 // basically accepts a pannel...
31649 // can accept a layout region..!?!?
31650 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31653 // theory? children can only be panels??
31655 //if (!cfg.xtype.match(/Panel$/)) {
31660 if (typeof(cfg.region) == 'undefined') {
31661 Roo.log("Failed to add Panel, region was not set");
31665 var region = cfg.region;
31671 xitems = cfg.items;
31678 case 'Content': // ContentPanel (el, cfg)
31679 case 'Scroll': // ContentPanel (el, cfg)
31681 cfg.autoCreate = true;
31682 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31684 // var el = this.el.createChild();
31685 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31688 this.add(region, ret);
31692 case 'TreePanel': // our new panel!
31693 cfg.el = this.el.createChild();
31694 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31695 this.add(region, ret);
31700 // create a new Layout (which is a Border Layout...
31702 var clayout = cfg.layout;
31703 clayout.el = this.el.createChild();
31704 clayout.items = clayout.items || [];
31708 // replace this exitems with the clayout ones..
31709 xitems = clayout.items;
31711 // force background off if it's in center...
31712 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31713 cfg.background = false;
31715 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
31718 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31719 //console.log('adding nested layout panel ' + cfg.toSource());
31720 this.add(region, ret);
31721 nb = {}; /// find first...
31726 // needs grid and region
31728 //var el = this.getRegion(region).el.createChild();
31730 *var el = this.el.createChild();
31731 // create the grid first...
31732 cfg.grid.container = el;
31733 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31736 if (region == 'center' && this.active ) {
31737 cfg.background = false;
31740 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31742 this.add(region, ret);
31744 if (cfg.background) {
31745 // render grid on panel activation (if panel background)
31746 ret.on('activate', function(gp) {
31747 if (!gp.grid.rendered) {
31748 // gp.grid.render(el);
31752 // cfg.grid.render(el);
31758 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31759 // it was the old xcomponent building that caused this before.
31760 // espeically if border is the top element in the tree.
31770 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31772 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31773 this.add(region, ret);
31777 throw "Can not add '" + cfg.xtype + "' to Border";
31783 this.beginUpdate();
31787 Roo.each(xitems, function(i) {
31788 region = nb && i.region ? i.region : false;
31790 var add = ret.addxtype(i);
31793 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31794 if (!i.background) {
31795 abn[region] = nb[region] ;
31802 // make the last non-background panel active..
31803 //if (nb) { Roo.log(abn); }
31806 for(var r in abn) {
31807 region = this.getRegion(r);
31809 // tried using nb[r], but it does not work..
31811 region.showPanel(abn[r]);
31822 factory : function(cfg)
31825 var validRegions = Roo.bootstrap.layout.Border.regions;
31827 var target = cfg.region;
31830 var r = Roo.bootstrap.layout;
31834 return new r.North(cfg);
31836 return new r.South(cfg);
31838 return new r.East(cfg);
31840 return new r.West(cfg);
31842 return new r.Center(cfg);
31844 throw 'Layout region "'+target+'" not supported.';
31851 * Ext JS Library 1.1.1
31852 * Copyright(c) 2006-2007, Ext JS, LLC.
31854 * Originally Released Under LGPL - original licence link has changed is not relivant.
31857 * <script type="text/javascript">
31861 * @class Roo.bootstrap.layout.Basic
31862 * @extends Roo.util.Observable
31863 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31864 * and does not have a titlebar, tabs or any other features. All it does is size and position
31865 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31866 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
31867 * @cfg {string} region the region that it inhabits..
31868 * @cfg {bool} skipConfig skip config?
31872 Roo.bootstrap.layout.Basic = function(config){
31874 this.mgr = config.mgr;
31876 this.position = config.region;
31878 var skipConfig = config.skipConfig;
31882 * @scope Roo.BasicLayoutRegion
31886 * @event beforeremove
31887 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31888 * @param {Roo.LayoutRegion} this
31889 * @param {Roo.ContentPanel} panel The panel
31890 * @param {Object} e The cancel event object
31892 "beforeremove" : true,
31894 * @event invalidated
31895 * Fires when the layout for this region is changed.
31896 * @param {Roo.LayoutRegion} this
31898 "invalidated" : true,
31900 * @event visibilitychange
31901 * Fires when this region is shown or hidden
31902 * @param {Roo.LayoutRegion} this
31903 * @param {Boolean} visibility true or false
31905 "visibilitychange" : true,
31907 * @event paneladded
31908 * Fires when a panel is added.
31909 * @param {Roo.LayoutRegion} this
31910 * @param {Roo.ContentPanel} panel The panel
31912 "paneladded" : true,
31914 * @event panelremoved
31915 * Fires when a panel is removed.
31916 * @param {Roo.LayoutRegion} this
31917 * @param {Roo.ContentPanel} panel The panel
31919 "panelremoved" : true,
31921 * @event beforecollapse
31922 * Fires when this region before collapse.
31923 * @param {Roo.LayoutRegion} this
31925 "beforecollapse" : true,
31928 * Fires when this region is collapsed.
31929 * @param {Roo.LayoutRegion} this
31931 "collapsed" : true,
31934 * Fires when this region is expanded.
31935 * @param {Roo.LayoutRegion} this
31940 * Fires when this region is slid into view.
31941 * @param {Roo.LayoutRegion} this
31943 "slideshow" : true,
31946 * Fires when this region slides out of view.
31947 * @param {Roo.LayoutRegion} this
31949 "slidehide" : true,
31951 * @event panelactivated
31952 * Fires when a panel is activated.
31953 * @param {Roo.LayoutRegion} this
31954 * @param {Roo.ContentPanel} panel The activated panel
31956 "panelactivated" : true,
31959 * Fires when the user resizes this region.
31960 * @param {Roo.LayoutRegion} this
31961 * @param {Number} newSize The new size (width for east/west, height for north/south)
31965 /** A collection of panels in this region. @type Roo.util.MixedCollection */
31966 this.panels = new Roo.util.MixedCollection();
31967 this.panels.getKey = this.getPanelId.createDelegate(this);
31969 this.activePanel = null;
31970 // ensure listeners are added...
31972 if (config.listeners || config.events) {
31973 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
31974 listeners : config.listeners || {},
31975 events : config.events || {}
31979 if(skipConfig !== true){
31980 this.applyConfig(config);
31984 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
31986 getPanelId : function(p){
31990 applyConfig : function(config){
31991 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31992 this.config = config;
31997 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
31998 * the width, for horizontal (north, south) the height.
31999 * @param {Number} newSize The new width or height
32001 resizeTo : function(newSize){
32002 var el = this.el ? this.el :
32003 (this.activePanel ? this.activePanel.getEl() : null);
32005 switch(this.position){
32008 el.setWidth(newSize);
32009 this.fireEvent("resized", this, newSize);
32013 el.setHeight(newSize);
32014 this.fireEvent("resized", this, newSize);
32020 getBox : function(){
32021 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32024 getMargins : function(){
32025 return this.margins;
32028 updateBox : function(box){
32030 var el = this.activePanel.getEl();
32031 el.dom.style.left = box.x + "px";
32032 el.dom.style.top = box.y + "px";
32033 this.activePanel.setSize(box.width, box.height);
32037 * Returns the container element for this region.
32038 * @return {Roo.Element}
32040 getEl : function(){
32041 return this.activePanel;
32045 * Returns true if this region is currently visible.
32046 * @return {Boolean}
32048 isVisible : function(){
32049 return this.activePanel ? true : false;
32052 setActivePanel : function(panel){
32053 panel = this.getPanel(panel);
32054 if(this.activePanel && this.activePanel != panel){
32055 this.activePanel.setActiveState(false);
32056 this.activePanel.getEl().setLeftTop(-10000,-10000);
32058 this.activePanel = panel;
32059 panel.setActiveState(true);
32061 panel.setSize(this.box.width, this.box.height);
32063 this.fireEvent("panelactivated", this, panel);
32064 this.fireEvent("invalidated");
32068 * Show the specified panel.
32069 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32070 * @return {Roo.ContentPanel} The shown panel or null
32072 showPanel : function(panel){
32073 panel = this.getPanel(panel);
32075 this.setActivePanel(panel);
32081 * Get the active panel for this region.
32082 * @return {Roo.ContentPanel} The active panel or null
32084 getActivePanel : function(){
32085 return this.activePanel;
32089 * Add the passed ContentPanel(s)
32090 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32091 * @return {Roo.ContentPanel} The panel added (if only one was added)
32093 add : function(panel){
32094 if(arguments.length > 1){
32095 for(var i = 0, len = arguments.length; i < len; i++) {
32096 this.add(arguments[i]);
32100 if(this.hasPanel(panel)){
32101 this.showPanel(panel);
32104 var el = panel.getEl();
32105 if(el.dom.parentNode != this.mgr.el.dom){
32106 this.mgr.el.dom.appendChild(el.dom);
32108 if(panel.setRegion){
32109 panel.setRegion(this);
32111 this.panels.add(panel);
32112 el.setStyle("position", "absolute");
32113 if(!panel.background){
32114 this.setActivePanel(panel);
32115 if(this.config.initialSize && this.panels.getCount()==1){
32116 this.resizeTo(this.config.initialSize);
32119 this.fireEvent("paneladded", this, panel);
32124 * Returns true if the panel is in this region.
32125 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32126 * @return {Boolean}
32128 hasPanel : function(panel){
32129 if(typeof panel == "object"){ // must be panel obj
32130 panel = panel.getId();
32132 return this.getPanel(panel) ? true : false;
32136 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32137 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32138 * @param {Boolean} preservePanel Overrides the config preservePanel option
32139 * @return {Roo.ContentPanel} The panel that was removed
32141 remove : function(panel, preservePanel){
32142 panel = this.getPanel(panel);
32147 this.fireEvent("beforeremove", this, panel, e);
32148 if(e.cancel === true){
32151 var panelId = panel.getId();
32152 this.panels.removeKey(panelId);
32157 * Returns the panel specified or null if it's not in this region.
32158 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32159 * @return {Roo.ContentPanel}
32161 getPanel : function(id){
32162 if(typeof id == "object"){ // must be panel obj
32165 return this.panels.get(id);
32169 * Returns this regions position (north/south/east/west/center).
32172 getPosition: function(){
32173 return this.position;
32177 * Ext JS Library 1.1.1
32178 * Copyright(c) 2006-2007, Ext JS, LLC.
32180 * Originally Released Under LGPL - original licence link has changed is not relivant.
32183 * <script type="text/javascript">
32187 * @class Roo.bootstrap.layout.Region
32188 * @extends Roo.bootstrap.layout.Basic
32189 * This class represents a region in a layout manager.
32191 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32192 * @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})
32193 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
32194 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
32195 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
32196 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
32197 * @cfg {String} title The title for the region (overrides panel titles)
32198 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
32199 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32200 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
32201 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32202 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
32203 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32204 * the space available, similar to FireFox 1.5 tabs (defaults to false)
32205 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
32206 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
32207 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
32209 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
32210 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
32211 * @cfg {Boolean} disableTabTips True to disable tab tooltips
32212 * @cfg {Number} width For East/West panels
32213 * @cfg {Number} height For North/South panels
32214 * @cfg {Boolean} split To show the splitter
32215 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
32217 * @cfg {string} cls Extra CSS classes to add to region
32219 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32220 * @cfg {string} region the region that it inhabits..
32223 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
32224 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
32226 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
32227 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
32228 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
32230 Roo.bootstrap.layout.Region = function(config)
32232 this.applyConfig(config);
32234 var mgr = config.mgr;
32235 var pos = config.region;
32236 config.skipConfig = true;
32237 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32240 this.onRender(mgr.el);
32243 this.visible = true;
32244 this.collapsed = false;
32247 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32249 position: '', // set by wrapper (eg. north/south etc..)
32251 createBody : function(){
32252 /** This region's body element
32253 * @type Roo.Element */
32254 this.bodyEl = this.el.createChild({
32256 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32260 onRender: function(ctr, pos)
32262 var dh = Roo.DomHelper;
32263 /** This region's container element
32264 * @type Roo.Element */
32265 this.el = dh.append(ctr.dom, {
32267 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32269 /** This region's title element
32270 * @type Roo.Element */
32272 this.titleEl = dh.append(this.el.dom,
32275 unselectable: "on",
32276 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32278 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
32279 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32282 this.titleEl.enableDisplayMode();
32283 /** This region's title text element
32284 * @type HTMLElement */
32285 this.titleTextEl = this.titleEl.dom.firstChild;
32286 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32288 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32289 this.closeBtn.enableDisplayMode();
32290 this.closeBtn.on("click", this.closeClicked, this);
32291 this.closeBtn.hide();
32293 this.createBody(this.config);
32294 if(this.config.hideWhenEmpty){
32296 this.on("paneladded", this.validateVisibility, this);
32297 this.on("panelremoved", this.validateVisibility, this);
32299 if(this.autoScroll){
32300 this.bodyEl.setStyle("overflow", "auto");
32302 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32304 //if(c.titlebar !== false){
32305 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32306 this.titleEl.hide();
32308 this.titleEl.show();
32309 if(this.config.title){
32310 this.titleTextEl.innerHTML = this.config.title;
32314 if(this.config.collapsed){
32315 this.collapse(true);
32317 if(this.config.hidden){
32322 applyConfig : function(c)
32325 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32326 var dh = Roo.DomHelper;
32327 if(c.titlebar !== false){
32328 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32329 this.collapseBtn.on("click", this.collapse, this);
32330 this.collapseBtn.enableDisplayMode();
32332 if(c.showPin === true || this.showPin){
32333 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32334 this.stickBtn.enableDisplayMode();
32335 this.stickBtn.on("click", this.expand, this);
32336 this.stickBtn.hide();
32341 /** This region's collapsed element
32342 * @type Roo.Element */
32345 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32346 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32349 if(c.floatable !== false){
32350 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32351 this.collapsedEl.on("click", this.collapseClick, this);
32354 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32355 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32356 id: "message", unselectable: "on", style:{"float":"left"}});
32357 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32359 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32360 this.expandBtn.on("click", this.expand, this);
32364 if(this.collapseBtn){
32365 this.collapseBtn.setVisible(c.collapsible == true);
32368 this.cmargins = c.cmargins || this.cmargins ||
32369 (this.position == "west" || this.position == "east" ?
32370 {top: 0, left: 2, right:2, bottom: 0} :
32371 {top: 2, left: 0, right:0, bottom: 2});
32373 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32376 this.bottomTabs = c.tabPosition != "top";
32378 this.autoScroll = c.autoScroll || false;
32383 this.duration = c.duration || .30;
32384 this.slideDuration = c.slideDuration || .45;
32389 * Returns true if this region is currently visible.
32390 * @return {Boolean}
32392 isVisible : function(){
32393 return this.visible;
32397 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32398 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
32400 //setCollapsedTitle : function(title){
32401 // title = title || " ";
32402 // if(this.collapsedTitleTextEl){
32403 // this.collapsedTitleTextEl.innerHTML = title;
32407 getBox : function(){
32409 // if(!this.collapsed){
32410 b = this.el.getBox(false, true);
32412 // b = this.collapsedEl.getBox(false, true);
32417 getMargins : function(){
32418 return this.margins;
32419 //return this.collapsed ? this.cmargins : this.margins;
32422 highlight : function(){
32423 this.el.addClass("x-layout-panel-dragover");
32426 unhighlight : function(){
32427 this.el.removeClass("x-layout-panel-dragover");
32430 updateBox : function(box)
32433 if(!this.collapsed){
32434 this.el.dom.style.left = box.x + "px";
32435 this.el.dom.style.top = box.y + "px";
32436 this.updateBody(box.width, box.height);
32438 this.collapsedEl.dom.style.left = box.x + "px";
32439 this.collapsedEl.dom.style.top = box.y + "px";
32440 this.collapsedEl.setSize(box.width, box.height);
32443 this.tabs.autoSizeTabs();
32447 updateBody : function(w, h)
32450 this.el.setWidth(w);
32451 w -= this.el.getBorderWidth("rl");
32452 if(this.config.adjustments){
32453 w += this.config.adjustments[0];
32457 this.el.setHeight(h);
32458 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32459 h -= this.el.getBorderWidth("tb");
32460 if(this.config.adjustments){
32461 h += this.config.adjustments[1];
32463 this.bodyEl.setHeight(h);
32465 h = this.tabs.syncHeight(h);
32468 if(this.panelSize){
32469 w = w !== null ? w : this.panelSize.width;
32470 h = h !== null ? h : this.panelSize.height;
32472 if(this.activePanel){
32473 var el = this.activePanel.getEl();
32474 w = w !== null ? w : el.getWidth();
32475 h = h !== null ? h : el.getHeight();
32476 this.panelSize = {width: w, height: h};
32477 this.activePanel.setSize(w, h);
32479 if(Roo.isIE && this.tabs){
32480 this.tabs.el.repaint();
32485 * Returns the container element for this region.
32486 * @return {Roo.Element}
32488 getEl : function(){
32493 * Hides this region.
32496 //if(!this.collapsed){
32497 this.el.dom.style.left = "-2000px";
32500 // this.collapsedEl.dom.style.left = "-2000px";
32501 // this.collapsedEl.hide();
32503 this.visible = false;
32504 this.fireEvent("visibilitychange", this, false);
32508 * Shows this region if it was previously hidden.
32511 //if(!this.collapsed){
32514 // this.collapsedEl.show();
32516 this.visible = true;
32517 this.fireEvent("visibilitychange", this, true);
32520 closeClicked : function(){
32521 if(this.activePanel){
32522 this.remove(this.activePanel);
32526 collapseClick : function(e){
32528 e.stopPropagation();
32531 e.stopPropagation();
32537 * Collapses this region.
32538 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32541 collapse : function(skipAnim, skipCheck = false){
32542 if(this.collapsed) {
32546 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32548 this.collapsed = true;
32550 this.split.el.hide();
32552 if(this.config.animate && skipAnim !== true){
32553 this.fireEvent("invalidated", this);
32554 this.animateCollapse();
32556 this.el.setLocation(-20000,-20000);
32558 this.collapsedEl.show();
32559 this.fireEvent("collapsed", this);
32560 this.fireEvent("invalidated", this);
32566 animateCollapse : function(){
32571 * Expands this region if it was previously collapsed.
32572 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32573 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32576 expand : function(e, skipAnim){
32578 e.stopPropagation();
32580 if(!this.collapsed || this.el.hasActiveFx()) {
32584 this.afterSlideIn();
32587 this.collapsed = false;
32588 if(this.config.animate && skipAnim !== true){
32589 this.animateExpand();
32593 this.split.el.show();
32595 this.collapsedEl.setLocation(-2000,-2000);
32596 this.collapsedEl.hide();
32597 this.fireEvent("invalidated", this);
32598 this.fireEvent("expanded", this);
32602 animateExpand : function(){
32606 initTabs : function()
32608 this.bodyEl.setStyle("overflow", "hidden");
32609 var ts = new Roo.bootstrap.panel.Tabs({
32610 el: this.bodyEl.dom,
32611 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32612 disableTooltips: this.config.disableTabTips,
32613 toolbar : this.config.toolbar
32616 if(this.config.hideTabs){
32617 ts.stripWrap.setDisplayed(false);
32620 ts.resizeTabs = this.config.resizeTabs === true;
32621 ts.minTabWidth = this.config.minTabWidth || 40;
32622 ts.maxTabWidth = this.config.maxTabWidth || 250;
32623 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32624 ts.monitorResize = false;
32625 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32626 ts.bodyEl.addClass('roo-layout-tabs-body');
32627 this.panels.each(this.initPanelAsTab, this);
32630 initPanelAsTab : function(panel){
32631 var ti = this.tabs.addTab(
32633 panel.getTitle(), null,
32634 this.config.closeOnTab && panel.isClosable()
32636 if(panel.tabTip !== undefined){
32637 ti.setTooltip(panel.tabTip);
32639 ti.on("activate", function(){
32640 this.setActivePanel(panel);
32643 if(this.config.closeOnTab){
32644 ti.on("beforeclose", function(t, e){
32646 this.remove(panel);
32652 updatePanelTitle : function(panel, title)
32654 if(this.activePanel == panel){
32655 this.updateTitle(title);
32658 var ti = this.tabs.getTab(panel.getEl().id);
32660 if(panel.tabTip !== undefined){
32661 ti.setTooltip(panel.tabTip);
32666 updateTitle : function(title){
32667 if(this.titleTextEl && !this.config.title){
32668 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
32672 setActivePanel : function(panel)
32674 panel = this.getPanel(panel);
32675 if(this.activePanel && this.activePanel != panel){
32676 this.activePanel.setActiveState(false);
32678 this.activePanel = panel;
32679 panel.setActiveState(true);
32680 if(this.panelSize){
32681 panel.setSize(this.panelSize.width, this.panelSize.height);
32684 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32686 this.updateTitle(panel.getTitle());
32688 this.fireEvent("invalidated", this);
32690 this.fireEvent("panelactivated", this, panel);
32694 * Shows the specified panel.
32695 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32696 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32698 showPanel : function(panel)
32700 panel = this.getPanel(panel);
32703 var tab = this.tabs.getTab(panel.getEl().id);
32704 if(tab.isHidden()){
32705 this.tabs.unhideTab(tab.id);
32709 this.setActivePanel(panel);
32716 * Get the active panel for this region.
32717 * @return {Roo.ContentPanel} The active panel or null
32719 getActivePanel : function(){
32720 return this.activePanel;
32723 validateVisibility : function(){
32724 if(this.panels.getCount() < 1){
32725 this.updateTitle(" ");
32726 this.closeBtn.hide();
32729 if(!this.isVisible()){
32736 * Adds the passed ContentPanel(s) to this region.
32737 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32738 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32740 add : function(panel){
32741 if(arguments.length > 1){
32742 for(var i = 0, len = arguments.length; i < len; i++) {
32743 this.add(arguments[i]);
32747 if(this.hasPanel(panel)){
32748 this.showPanel(panel);
32751 panel.setRegion(this);
32752 this.panels.add(panel);
32753 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32754 this.bodyEl.dom.appendChild(panel.getEl().dom);
32755 if(panel.background !== true){
32756 this.setActivePanel(panel);
32758 this.fireEvent("paneladded", this, panel);
32764 this.initPanelAsTab(panel);
32768 if(panel.background !== true){
32769 this.tabs.activate(panel.getEl().id);
32771 this.fireEvent("paneladded", this, panel);
32776 * Hides the tab for the specified panel.
32777 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32779 hidePanel : function(panel){
32780 if(this.tabs && (panel = this.getPanel(panel))){
32781 this.tabs.hideTab(panel.getEl().id);
32786 * Unhides the tab for a previously hidden panel.
32787 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32789 unhidePanel : function(panel){
32790 if(this.tabs && (panel = this.getPanel(panel))){
32791 this.tabs.unhideTab(panel.getEl().id);
32795 clearPanels : function(){
32796 while(this.panels.getCount() > 0){
32797 this.remove(this.panels.first());
32802 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32803 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32804 * @param {Boolean} preservePanel Overrides the config preservePanel option
32805 * @return {Roo.ContentPanel} The panel that was removed
32807 remove : function(panel, preservePanel)
32809 panel = this.getPanel(panel);
32814 this.fireEvent("beforeremove", this, panel, e);
32815 if(e.cancel === true){
32818 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32819 var panelId = panel.getId();
32820 this.panels.removeKey(panelId);
32822 document.body.appendChild(panel.getEl().dom);
32825 this.tabs.removeTab(panel.getEl().id);
32826 }else if (!preservePanel){
32827 this.bodyEl.dom.removeChild(panel.getEl().dom);
32829 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32830 var p = this.panels.first();
32831 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32832 tempEl.appendChild(p.getEl().dom);
32833 this.bodyEl.update("");
32834 this.bodyEl.dom.appendChild(p.getEl().dom);
32836 this.updateTitle(p.getTitle());
32838 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32839 this.setActivePanel(p);
32841 panel.setRegion(null);
32842 if(this.activePanel == panel){
32843 this.activePanel = null;
32845 if(this.config.autoDestroy !== false && preservePanel !== true){
32846 try{panel.destroy();}catch(e){}
32848 this.fireEvent("panelremoved", this, panel);
32853 * Returns the TabPanel component used by this region
32854 * @return {Roo.TabPanel}
32856 getTabs : function(){
32860 createTool : function(parentEl, className){
32861 var btn = Roo.DomHelper.append(parentEl, {
32863 cls: "x-layout-tools-button",
32866 cls: "roo-layout-tools-button-inner " + className,
32870 btn.addClassOnOver("roo-layout-tools-button-over");
32875 * Ext JS Library 1.1.1
32876 * Copyright(c) 2006-2007, Ext JS, LLC.
32878 * Originally Released Under LGPL - original licence link has changed is not relivant.
32881 * <script type="text/javascript">
32887 * @class Roo.SplitLayoutRegion
32888 * @extends Roo.LayoutRegion
32889 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32891 Roo.bootstrap.layout.Split = function(config){
32892 this.cursor = config.cursor;
32893 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32896 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32898 splitTip : "Drag to resize.",
32899 collapsibleSplitTip : "Drag to resize. Double click to hide.",
32900 useSplitTips : false,
32902 applyConfig : function(config){
32903 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32906 onRender : function(ctr,pos) {
32908 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
32909 if(!this.config.split){
32914 var splitEl = Roo.DomHelper.append(ctr.dom, {
32916 id: this.el.id + "-split",
32917 cls: "roo-layout-split roo-layout-split-"+this.position,
32920 /** The SplitBar for this region
32921 * @type Roo.SplitBar */
32922 // does not exist yet...
32923 Roo.log([this.position, this.orientation]);
32925 this.split = new Roo.bootstrap.SplitBar({
32926 dragElement : splitEl,
32927 resizingElement: this.el,
32928 orientation : this.orientation
32931 this.split.on("moved", this.onSplitMove, this);
32932 this.split.useShim = this.config.useShim === true;
32933 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32934 if(this.useSplitTips){
32935 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32937 //if(config.collapsible){
32938 // this.split.el.on("dblclick", this.collapse, this);
32941 if(typeof this.config.minSize != "undefined"){
32942 this.split.minSize = this.config.minSize;
32944 if(typeof this.config.maxSize != "undefined"){
32945 this.split.maxSize = this.config.maxSize;
32947 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
32948 this.hideSplitter();
32953 getHMaxSize : function(){
32954 var cmax = this.config.maxSize || 10000;
32955 var center = this.mgr.getRegion("center");
32956 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32959 getVMaxSize : function(){
32960 var cmax = this.config.maxSize || 10000;
32961 var center = this.mgr.getRegion("center");
32962 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32965 onSplitMove : function(split, newSize){
32966 this.fireEvent("resized", this, newSize);
32970 * Returns the {@link Roo.SplitBar} for this region.
32971 * @return {Roo.SplitBar}
32973 getSplitBar : function(){
32978 this.hideSplitter();
32979 Roo.bootstrap.layout.Split.superclass.hide.call(this);
32982 hideSplitter : function(){
32984 this.split.el.setLocation(-2000,-2000);
32985 this.split.el.hide();
32991 this.split.el.show();
32993 Roo.bootstrap.layout.Split.superclass.show.call(this);
32996 beforeSlide: function(){
32997 if(Roo.isGecko){// firefox overflow auto bug workaround
32998 this.bodyEl.clip();
33000 this.tabs.bodyEl.clip();
33002 if(this.activePanel){
33003 this.activePanel.getEl().clip();
33005 if(this.activePanel.beforeSlide){
33006 this.activePanel.beforeSlide();
33012 afterSlide : function(){
33013 if(Roo.isGecko){// firefox overflow auto bug workaround
33014 this.bodyEl.unclip();
33016 this.tabs.bodyEl.unclip();
33018 if(this.activePanel){
33019 this.activePanel.getEl().unclip();
33020 if(this.activePanel.afterSlide){
33021 this.activePanel.afterSlide();
33027 initAutoHide : function(){
33028 if(this.autoHide !== false){
33029 if(!this.autoHideHd){
33030 var st = new Roo.util.DelayedTask(this.slideIn, this);
33031 this.autoHideHd = {
33032 "mouseout": function(e){
33033 if(!e.within(this.el, true)){
33037 "mouseover" : function(e){
33043 this.el.on(this.autoHideHd);
33047 clearAutoHide : function(){
33048 if(this.autoHide !== false){
33049 this.el.un("mouseout", this.autoHideHd.mouseout);
33050 this.el.un("mouseover", this.autoHideHd.mouseover);
33054 clearMonitor : function(){
33055 Roo.get(document).un("click", this.slideInIf, this);
33058 // these names are backwards but not changed for compat
33059 slideOut : function(){
33060 if(this.isSlid || this.el.hasActiveFx()){
33063 this.isSlid = true;
33064 if(this.collapseBtn){
33065 this.collapseBtn.hide();
33067 this.closeBtnState = this.closeBtn.getStyle('display');
33068 this.closeBtn.hide();
33070 this.stickBtn.show();
33073 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33074 this.beforeSlide();
33075 this.el.setStyle("z-index", 10001);
33076 this.el.slideIn(this.getSlideAnchor(), {
33077 callback: function(){
33079 this.initAutoHide();
33080 Roo.get(document).on("click", this.slideInIf, this);
33081 this.fireEvent("slideshow", this);
33088 afterSlideIn : function(){
33089 this.clearAutoHide();
33090 this.isSlid = false;
33091 this.clearMonitor();
33092 this.el.setStyle("z-index", "");
33093 if(this.collapseBtn){
33094 this.collapseBtn.show();
33096 this.closeBtn.setStyle('display', this.closeBtnState);
33098 this.stickBtn.hide();
33100 this.fireEvent("slidehide", this);
33103 slideIn : function(cb){
33104 if(!this.isSlid || this.el.hasActiveFx()){
33108 this.isSlid = false;
33109 this.beforeSlide();
33110 this.el.slideOut(this.getSlideAnchor(), {
33111 callback: function(){
33112 this.el.setLeftTop(-10000, -10000);
33114 this.afterSlideIn();
33122 slideInIf : function(e){
33123 if(!e.within(this.el)){
33128 animateCollapse : function(){
33129 this.beforeSlide();
33130 this.el.setStyle("z-index", 20000);
33131 var anchor = this.getSlideAnchor();
33132 this.el.slideOut(anchor, {
33133 callback : function(){
33134 this.el.setStyle("z-index", "");
33135 this.collapsedEl.slideIn(anchor, {duration:.3});
33137 this.el.setLocation(-10000,-10000);
33139 this.fireEvent("collapsed", this);
33146 animateExpand : function(){
33147 this.beforeSlide();
33148 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33149 this.el.setStyle("z-index", 20000);
33150 this.collapsedEl.hide({
33153 this.el.slideIn(this.getSlideAnchor(), {
33154 callback : function(){
33155 this.el.setStyle("z-index", "");
33158 this.split.el.show();
33160 this.fireEvent("invalidated", this);
33161 this.fireEvent("expanded", this);
33189 getAnchor : function(){
33190 return this.anchors[this.position];
33193 getCollapseAnchor : function(){
33194 return this.canchors[this.position];
33197 getSlideAnchor : function(){
33198 return this.sanchors[this.position];
33201 getAlignAdj : function(){
33202 var cm = this.cmargins;
33203 switch(this.position){
33219 getExpandAdj : function(){
33220 var c = this.collapsedEl, cm = this.cmargins;
33221 switch(this.position){
33223 return [-(cm.right+c.getWidth()+cm.left), 0];
33226 return [cm.right+c.getWidth()+cm.left, 0];
33229 return [0, -(cm.top+cm.bottom+c.getHeight())];
33232 return [0, cm.top+cm.bottom+c.getHeight()];
33238 * Ext JS Library 1.1.1
33239 * Copyright(c) 2006-2007, Ext JS, LLC.
33241 * Originally Released Under LGPL - original licence link has changed is not relivant.
33244 * <script type="text/javascript">
33247 * These classes are private internal classes
33249 Roo.bootstrap.layout.Center = function(config){
33250 config.region = "center";
33251 Roo.bootstrap.layout.Region.call(this, config);
33252 this.visible = true;
33253 this.minWidth = config.minWidth || 20;
33254 this.minHeight = config.minHeight || 20;
33257 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33259 // center panel can't be hidden
33263 // center panel can't be hidden
33266 getMinWidth: function(){
33267 return this.minWidth;
33270 getMinHeight: function(){
33271 return this.minHeight;
33284 Roo.bootstrap.layout.North = function(config)
33286 config.region = 'north';
33287 config.cursor = 'n-resize';
33289 Roo.bootstrap.layout.Split.call(this, config);
33293 this.split.placement = Roo.bootstrap.SplitBar.TOP;
33294 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33295 this.split.el.addClass("roo-layout-split-v");
33297 var size = config.initialSize || config.height;
33298 if(typeof size != "undefined"){
33299 this.el.setHeight(size);
33302 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33304 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33308 getBox : function(){
33309 if(this.collapsed){
33310 return this.collapsedEl.getBox();
33312 var box = this.el.getBox();
33314 box.height += this.split.el.getHeight();
33319 updateBox : function(box){
33320 if(this.split && !this.collapsed){
33321 box.height -= this.split.el.getHeight();
33322 this.split.el.setLeft(box.x);
33323 this.split.el.setTop(box.y+box.height);
33324 this.split.el.setWidth(box.width);
33326 if(this.collapsed){
33327 this.updateBody(box.width, null);
33329 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33337 Roo.bootstrap.layout.South = function(config){
33338 config.region = 'south';
33339 config.cursor = 's-resize';
33340 Roo.bootstrap.layout.Split.call(this, config);
33342 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33343 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33344 this.split.el.addClass("roo-layout-split-v");
33346 var size = config.initialSize || config.height;
33347 if(typeof size != "undefined"){
33348 this.el.setHeight(size);
33352 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33353 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33354 getBox : function(){
33355 if(this.collapsed){
33356 return this.collapsedEl.getBox();
33358 var box = this.el.getBox();
33360 var sh = this.split.el.getHeight();
33367 updateBox : function(box){
33368 if(this.split && !this.collapsed){
33369 var sh = this.split.el.getHeight();
33372 this.split.el.setLeft(box.x);
33373 this.split.el.setTop(box.y-sh);
33374 this.split.el.setWidth(box.width);
33376 if(this.collapsed){
33377 this.updateBody(box.width, null);
33379 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33383 Roo.bootstrap.layout.East = function(config){
33384 config.region = "east";
33385 config.cursor = "e-resize";
33386 Roo.bootstrap.layout.Split.call(this, config);
33388 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33389 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33390 this.split.el.addClass("roo-layout-split-h");
33392 var size = config.initialSize || config.width;
33393 if(typeof size != "undefined"){
33394 this.el.setWidth(size);
33397 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33398 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33399 getBox : function(){
33400 if(this.collapsed){
33401 return this.collapsedEl.getBox();
33403 var box = this.el.getBox();
33405 var sw = this.split.el.getWidth();
33412 updateBox : function(box){
33413 if(this.split && !this.collapsed){
33414 var sw = this.split.el.getWidth();
33416 this.split.el.setLeft(box.x);
33417 this.split.el.setTop(box.y);
33418 this.split.el.setHeight(box.height);
33421 if(this.collapsed){
33422 this.updateBody(null, box.height);
33424 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33428 Roo.bootstrap.layout.West = function(config){
33429 config.region = "west";
33430 config.cursor = "w-resize";
33432 Roo.bootstrap.layout.Split.call(this, config);
33434 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33435 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33436 this.split.el.addClass("roo-layout-split-h");
33440 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33441 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33443 onRender: function(ctr, pos)
33445 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33446 var size = this.config.initialSize || this.config.width;
33447 if(typeof size != "undefined"){
33448 this.el.setWidth(size);
33452 getBox : function(){
33453 if(this.collapsed){
33454 return this.collapsedEl.getBox();
33456 var box = this.el.getBox();
33458 box.width += this.split.el.getWidth();
33463 updateBox : function(box){
33464 if(this.split && !this.collapsed){
33465 var sw = this.split.el.getWidth();
33467 this.split.el.setLeft(box.x+box.width);
33468 this.split.el.setTop(box.y);
33469 this.split.el.setHeight(box.height);
33471 if(this.collapsed){
33472 this.updateBody(null, box.height);
33474 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33477 Roo.namespace("Roo.bootstrap.panel");/*
33479 * Ext JS Library 1.1.1
33480 * Copyright(c) 2006-2007, Ext JS, LLC.
33482 * Originally Released Under LGPL - original licence link has changed is not relivant.
33485 * <script type="text/javascript">
33488 * @class Roo.ContentPanel
33489 * @extends Roo.util.Observable
33490 * A basic ContentPanel element.
33491 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
33492 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
33493 * @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
33494 * @cfg {Boolean} closable True if the panel can be closed/removed
33495 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
33496 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33497 * @cfg {Toolbar} toolbar A toolbar for this panel
33498 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
33499 * @cfg {String} title The title for this panel
33500 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33501 * @cfg {String} url Calls {@link #setUrl} with this value
33502 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33503 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
33504 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
33505 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
33508 * Create a new ContentPanel.
33509 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33510 * @param {String/Object} config A string to set only the title or a config object
33511 * @param {String} content (optional) Set the HTML content for this panel
33512 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33514 Roo.bootstrap.panel.Content = function( config){
33516 var el = config.el;
33517 var content = config.content;
33519 if(config.autoCreate){ // xtype is available if this is called from factory
33522 this.el = Roo.get(el);
33523 if(!this.el && config && config.autoCreate){
33524 if(typeof config.autoCreate == "object"){
33525 if(!config.autoCreate.id){
33526 config.autoCreate.id = config.id||el;
33528 this.el = Roo.DomHelper.append(document.body,
33529 config.autoCreate, true);
33531 var elcfg = { tag: "div",
33532 cls: "roo-layout-inactive-content",
33536 elcfg.html = config.html;
33540 this.el = Roo.DomHelper.append(document.body, elcfg , true);
33543 this.closable = false;
33544 this.loaded = false;
33545 this.active = false;
33548 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33550 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33552 this.wrapEl = this.el.wrap();
33554 if (config.toolbar.items) {
33555 ti = config.toolbar.items ;
33556 delete config.toolbar.items ;
33560 this.toolbar.render(this.wrapEl, 'before');
33561 for(var i =0;i < ti.length;i++) {
33562 // Roo.log(['add child', items[i]]);
33563 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33565 this.toolbar.items = nitems;
33566 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33567 delete config.toolbar;
33571 // xtype created footer. - not sure if will work as we normally have to render first..
33572 if (this.footer && !this.footer.el && this.footer.xtype) {
33573 if (!this.wrapEl) {
33574 this.wrapEl = this.el.wrap();
33577 this.footer.container = this.wrapEl.createChild();
33579 this.footer = Roo.factory(this.footer, Roo);
33584 if(typeof config == "string"){
33585 this.title = config;
33587 Roo.apply(this, config);
33591 this.resizeEl = Roo.get(this.resizeEl, true);
33593 this.resizeEl = this.el;
33595 // handle view.xtype
33603 * Fires when this panel is activated.
33604 * @param {Roo.ContentPanel} this
33608 * @event deactivate
33609 * Fires when this panel is activated.
33610 * @param {Roo.ContentPanel} this
33612 "deactivate" : true,
33616 * Fires when this panel is resized if fitToFrame is true.
33617 * @param {Roo.ContentPanel} this
33618 * @param {Number} width The width after any component adjustments
33619 * @param {Number} height The height after any component adjustments
33625 * Fires when this tab is created
33626 * @param {Roo.ContentPanel} this
33637 if(this.autoScroll){
33638 this.resizeEl.setStyle("overflow", "auto");
33640 // fix randome scrolling
33641 //this.el.on('scroll', function() {
33642 // Roo.log('fix random scolling');
33643 // this.scrollTo('top',0);
33646 content = content || this.content;
33648 this.setContent(content);
33650 if(config && config.url){
33651 this.setUrl(this.url, this.params, this.loadOnce);
33656 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33658 if (this.view && typeof(this.view.xtype) != 'undefined') {
33659 this.view.el = this.el.appendChild(document.createElement("div"));
33660 this.view = Roo.factory(this.view);
33661 this.view.render && this.view.render(false, '');
33665 this.fireEvent('render', this);
33668 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33670 setRegion : function(region){
33671 this.region = region;
33673 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33675 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33680 * Returns the toolbar for this Panel if one was configured.
33681 * @return {Roo.Toolbar}
33683 getToolbar : function(){
33684 return this.toolbar;
33687 setActiveState : function(active){
33688 this.active = active;
33690 this.fireEvent("deactivate", this);
33692 this.fireEvent("activate", this);
33696 * Updates this panel's element
33697 * @param {String} content The new content
33698 * @param {Boolean} loadScripts (optional) true to look for and process scripts
33700 setContent : function(content, loadScripts){
33701 this.el.update(content, loadScripts);
33704 ignoreResize : function(w, h){
33705 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33708 this.lastSize = {width: w, height: h};
33713 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33714 * @return {Roo.UpdateManager} The UpdateManager
33716 getUpdateManager : function(){
33717 return this.el.getUpdateManager();
33720 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33721 * @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:
33724 url: "your-url.php",
33725 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33726 callback: yourFunction,
33727 scope: yourObject, //(optional scope)
33730 text: "Loading...",
33735 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33736 * 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.
33737 * @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}
33738 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33739 * @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.
33740 * @return {Roo.ContentPanel} this
33743 var um = this.el.getUpdateManager();
33744 um.update.apply(um, arguments);
33750 * 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.
33751 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33752 * @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)
33753 * @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)
33754 * @return {Roo.UpdateManager} The UpdateManager
33756 setUrl : function(url, params, loadOnce){
33757 if(this.refreshDelegate){
33758 this.removeListener("activate", this.refreshDelegate);
33760 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33761 this.on("activate", this.refreshDelegate);
33762 return this.el.getUpdateManager();
33765 _handleRefresh : function(url, params, loadOnce){
33766 if(!loadOnce || !this.loaded){
33767 var updater = this.el.getUpdateManager();
33768 updater.update(url, params, this._setLoaded.createDelegate(this));
33772 _setLoaded : function(){
33773 this.loaded = true;
33777 * Returns this panel's id
33780 getId : function(){
33785 * Returns this panel's element - used by regiosn to add.
33786 * @return {Roo.Element}
33788 getEl : function(){
33789 return this.wrapEl || this.el;
33794 adjustForComponents : function(width, height)
33796 //Roo.log('adjustForComponents ');
33797 if(this.resizeEl != this.el){
33798 width -= this.el.getFrameWidth('lr');
33799 height -= this.el.getFrameWidth('tb');
33802 var te = this.toolbar.getEl();
33803 height -= te.getHeight();
33804 te.setWidth(width);
33807 var te = this.footer.getEl();
33808 Roo.log("footer:" + te.getHeight());
33810 height -= te.getHeight();
33811 te.setWidth(width);
33815 if(this.adjustments){
33816 width += this.adjustments[0];
33817 height += this.adjustments[1];
33819 return {"width": width, "height": height};
33822 setSize : function(width, height){
33823 if(this.fitToFrame && !this.ignoreResize(width, height)){
33824 if(this.fitContainer && this.resizeEl != this.el){
33825 this.el.setSize(width, height);
33827 var size = this.adjustForComponents(width, height);
33828 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33829 this.fireEvent('resize', this, size.width, size.height);
33834 * Returns this panel's title
33837 getTitle : function(){
33842 * Set this panel's title
33843 * @param {String} title
33845 setTitle : function(title){
33846 this.title = title;
33848 this.region.updatePanelTitle(this, title);
33853 * Returns true is this panel was configured to be closable
33854 * @return {Boolean}
33856 isClosable : function(){
33857 return this.closable;
33860 beforeSlide : function(){
33862 this.resizeEl.clip();
33865 afterSlide : function(){
33867 this.resizeEl.unclip();
33871 * Force a content refresh from the URL specified in the {@link #setUrl} method.
33872 * Will fail silently if the {@link #setUrl} method has not been called.
33873 * This does not activate the panel, just updates its content.
33875 refresh : function(){
33876 if(this.refreshDelegate){
33877 this.loaded = false;
33878 this.refreshDelegate();
33883 * Destroys this panel
33885 destroy : function(){
33886 this.el.removeAllListeners();
33887 var tempEl = document.createElement("span");
33888 tempEl.appendChild(this.el.dom);
33889 tempEl.innerHTML = "";
33895 * form - if the content panel contains a form - this is a reference to it.
33896 * @type {Roo.form.Form}
33900 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33901 * This contains a reference to it.
33907 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33917 * @param {Object} cfg Xtype definition of item to add.
33921 getChildContainer: function () {
33922 return this.getEl();
33927 var ret = new Roo.factory(cfg);
33932 if (cfg.xtype.match(/^Form$/)) {
33935 //if (this.footer) {
33936 // el = this.footer.container.insertSibling(false, 'before');
33938 el = this.el.createChild();
33941 this.form = new Roo.form.Form(cfg);
33944 if ( this.form.allItems.length) {
33945 this.form.render(el.dom);
33949 // should only have one of theses..
33950 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33951 // views.. should not be just added - used named prop 'view''
33953 cfg.el = this.el.appendChild(document.createElement("div"));
33956 var ret = new Roo.factory(cfg);
33958 ret.render && ret.render(false, ''); // render blank..
33968 * @class Roo.bootstrap.panel.Grid
33969 * @extends Roo.bootstrap.panel.Content
33971 * Create a new GridPanel.
33972 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
33973 * @param {Object} config A the config object
33979 Roo.bootstrap.panel.Grid = function(config)
33983 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33984 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
33986 config.el = this.wrapper;
33987 //this.el = this.wrapper;
33989 if (config.container) {
33990 // ctor'ed from a Border/panel.grid
33993 this.wrapper.setStyle("overflow", "hidden");
33994 this.wrapper.addClass('roo-grid-container');
33999 if(config.toolbar){
34000 var tool_el = this.wrapper.createChild();
34001 this.toolbar = Roo.factory(config.toolbar);
34003 if (config.toolbar.items) {
34004 ti = config.toolbar.items ;
34005 delete config.toolbar.items ;
34009 this.toolbar.render(tool_el);
34010 for(var i =0;i < ti.length;i++) {
34011 // Roo.log(['add child', items[i]]);
34012 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34014 this.toolbar.items = nitems;
34016 delete config.toolbar;
34019 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34020 config.grid.scrollBody = true;;
34021 config.grid.monitorWindowResize = false; // turn off autosizing
34022 config.grid.autoHeight = false;
34023 config.grid.autoWidth = false;
34025 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34027 if (config.background) {
34028 // render grid on panel activation (if panel background)
34029 this.on('activate', function(gp) {
34030 if (!gp.grid.rendered) {
34031 gp.grid.render(el);
34032 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34038 this.grid.render(this.wrapper);
34039 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34042 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34043 // ??? needed ??? config.el = this.wrapper;
34048 // xtype created footer. - not sure if will work as we normally have to render first..
34049 if (this.footer && !this.footer.el && this.footer.xtype) {
34051 var ctr = this.grid.getView().getFooterPanel(true);
34052 this.footer.dataSource = this.grid.dataSource;
34053 this.footer = Roo.factory(this.footer, Roo);
34054 this.footer.render(ctr);
34064 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34065 getId : function(){
34066 return this.grid.id;
34070 * Returns the grid for this panel
34071 * @return {Roo.bootstrap.Table}
34073 getGrid : function(){
34077 setSize : function(width, height){
34078 if(!this.ignoreResize(width, height)){
34079 var grid = this.grid;
34080 var size = this.adjustForComponents(width, height);
34081 var gridel = grid.getGridEl();
34082 gridel.setSize(size.width, size.height);
34084 var thd = grid.getGridEl().select('thead',true).first();
34085 var tbd = grid.getGridEl().select('tbody', true).first();
34087 tbd.setSize(width, height - thd.getHeight());
34096 beforeSlide : function(){
34097 this.grid.getView().scroller.clip();
34100 afterSlide : function(){
34101 this.grid.getView().scroller.unclip();
34104 destroy : function(){
34105 this.grid.destroy();
34107 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
34112 * @class Roo.bootstrap.panel.Nest
34113 * @extends Roo.bootstrap.panel.Content
34115 * Create a new Panel, that can contain a layout.Border.
34118 * @param {Roo.BorderLayout} layout The layout for this panel
34119 * @param {String/Object} config A string to set only the title or a config object
34121 Roo.bootstrap.panel.Nest = function(config)
34123 // construct with only one argument..
34124 /* FIXME - implement nicer consturctors
34125 if (layout.layout) {
34127 layout = config.layout;
34128 delete config.layout;
34130 if (layout.xtype && !layout.getEl) {
34131 // then layout needs constructing..
34132 layout = Roo.factory(layout, Roo);
34136 config.el = config.layout.getEl();
34138 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34140 config.layout.monitorWindowResize = false; // turn off autosizing
34141 this.layout = config.layout;
34142 this.layout.getEl().addClass("roo-layout-nested-layout");
34149 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34151 setSize : function(width, height){
34152 if(!this.ignoreResize(width, height)){
34153 var size = this.adjustForComponents(width, height);
34154 var el = this.layout.getEl();
34155 el.setSize(size.width, size.height);
34156 var touch = el.dom.offsetWidth;
34157 this.layout.layout();
34158 // ie requires a double layout on the first pass
34159 if(Roo.isIE && !this.initialized){
34160 this.initialized = true;
34161 this.layout.layout();
34166 // activate all subpanels if not currently active..
34168 setActiveState : function(active){
34169 this.active = active;
34171 this.fireEvent("deactivate", this);
34175 this.fireEvent("activate", this);
34176 // not sure if this should happen before or after..
34177 if (!this.layout) {
34178 return; // should not happen..
34181 for (var r in this.layout.regions) {
34182 reg = this.layout.getRegion(r);
34183 if (reg.getActivePanel()) {
34184 //reg.showPanel(reg.getActivePanel()); // force it to activate..
34185 reg.setActivePanel(reg.getActivePanel());
34188 if (!reg.panels.length) {
34191 reg.showPanel(reg.getPanel(0));
34200 * Returns the nested BorderLayout for this panel
34201 * @return {Roo.BorderLayout}
34203 getLayout : function(){
34204 return this.layout;
34208 * Adds a xtype elements to the layout of the nested panel
34212 xtype : 'ContentPanel',
34219 xtype : 'NestedLayoutPanel',
34225 items : [ ... list of content panels or nested layout panels.. ]
34229 * @param {Object} cfg Xtype definition of item to add.
34231 addxtype : function(cfg) {
34232 return this.layout.addxtype(cfg);
34237 * Ext JS Library 1.1.1
34238 * Copyright(c) 2006-2007, Ext JS, LLC.
34240 * Originally Released Under LGPL - original licence link has changed is not relivant.
34243 * <script type="text/javascript">
34246 * @class Roo.TabPanel
34247 * @extends Roo.util.Observable
34248 * A lightweight tab container.
34252 // basic tabs 1, built from existing content
34253 var tabs = new Roo.TabPanel("tabs1");
34254 tabs.addTab("script", "View Script");
34255 tabs.addTab("markup", "View Markup");
34256 tabs.activate("script");
34258 // more advanced tabs, built from javascript
34259 var jtabs = new Roo.TabPanel("jtabs");
34260 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34262 // set up the UpdateManager
34263 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34264 var updater = tab2.getUpdateManager();
34265 updater.setDefaultUrl("ajax1.htm");
34266 tab2.on('activate', updater.refresh, updater, true);
34268 // Use setUrl for Ajax loading
34269 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34270 tab3.setUrl("ajax2.htm", null, true);
34273 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34276 jtabs.activate("jtabs-1");
34279 * Create a new TabPanel.
34280 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34281 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34283 Roo.bootstrap.panel.Tabs = function(config){
34285 * The container element for this TabPanel.
34286 * @type Roo.Element
34288 this.el = Roo.get(config.el);
34291 if(typeof config == "boolean"){
34292 this.tabPosition = config ? "bottom" : "top";
34294 Roo.apply(this, config);
34298 if(this.tabPosition == "bottom"){
34299 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34300 this.el.addClass("roo-tabs-bottom");
34302 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34303 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34304 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34306 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34308 if(this.tabPosition != "bottom"){
34309 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34310 * @type Roo.Element
34312 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34313 this.el.addClass("roo-tabs-top");
34317 this.bodyEl.setStyle("position", "relative");
34319 this.active = null;
34320 this.activateDelegate = this.activate.createDelegate(this);
34325 * Fires when the active tab changes
34326 * @param {Roo.TabPanel} this
34327 * @param {Roo.TabPanelItem} activePanel The new active tab
34331 * @event beforetabchange
34332 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34333 * @param {Roo.TabPanel} this
34334 * @param {Object} e Set cancel to true on this object to cancel the tab change
34335 * @param {Roo.TabPanelItem} tab The tab being changed to
34337 "beforetabchange" : true
34340 Roo.EventManager.onWindowResize(this.onResize, this);
34341 this.cpad = this.el.getPadding("lr");
34342 this.hiddenCount = 0;
34345 // toolbar on the tabbar support...
34346 if (this.toolbar) {
34347 alert("no toolbar support yet");
34348 this.toolbar = false;
34350 var tcfg = this.toolbar;
34351 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
34352 this.toolbar = new Roo.Toolbar(tcfg);
34353 if (Roo.isSafari) {
34354 var tbl = tcfg.container.child('table', true);
34355 tbl.setAttribute('width', '100%');
34363 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34366 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34368 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34370 tabPosition : "top",
34372 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34374 currentTabWidth : 0,
34376 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34380 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34384 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34386 preferredTabWidth : 175,
34388 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34390 resizeTabs : false,
34392 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34394 monitorResize : true,
34396 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
34401 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34402 * @param {String} id The id of the div to use <b>or create</b>
34403 * @param {String} text The text for the tab
34404 * @param {String} content (optional) Content to put in the TabPanelItem body
34405 * @param {Boolean} closable (optional) True to create a close icon on the tab
34406 * @return {Roo.TabPanelItem} The created TabPanelItem
34408 addTab : function(id, text, content, closable)
34410 var item = new Roo.bootstrap.panel.TabItem({
34414 closable : closable
34416 this.addTabItem(item);
34418 item.setContent(content);
34424 * Returns the {@link Roo.TabPanelItem} with the specified id/index
34425 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34426 * @return {Roo.TabPanelItem}
34428 getTab : function(id){
34429 return this.items[id];
34433 * Hides the {@link Roo.TabPanelItem} with the specified id/index
34434 * @param {String/Number} id The id or index of the TabPanelItem to hide.
34436 hideTab : function(id){
34437 var t = this.items[id];
34440 this.hiddenCount++;
34441 this.autoSizeTabs();
34446 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34447 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34449 unhideTab : function(id){
34450 var t = this.items[id];
34452 t.setHidden(false);
34453 this.hiddenCount--;
34454 this.autoSizeTabs();
34459 * Adds an existing {@link Roo.TabPanelItem}.
34460 * @param {Roo.TabPanelItem} item The TabPanelItem to add
34462 addTabItem : function(item){
34463 this.items[item.id] = item;
34464 this.items.push(item);
34465 // if(this.resizeTabs){
34466 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34467 // this.autoSizeTabs();
34469 // item.autoSize();
34474 * Removes a {@link Roo.TabPanelItem}.
34475 * @param {String/Number} id The id or index of the TabPanelItem to remove.
34477 removeTab : function(id){
34478 var items = this.items;
34479 var tab = items[id];
34480 if(!tab) { return; }
34481 var index = items.indexOf(tab);
34482 if(this.active == tab && items.length > 1){
34483 var newTab = this.getNextAvailable(index);
34488 this.stripEl.dom.removeChild(tab.pnode.dom);
34489 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34490 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34492 items.splice(index, 1);
34493 delete this.items[tab.id];
34494 tab.fireEvent("close", tab);
34495 tab.purgeListeners();
34496 this.autoSizeTabs();
34499 getNextAvailable : function(start){
34500 var items = this.items;
34502 // look for a next tab that will slide over to
34503 // replace the one being removed
34504 while(index < items.length){
34505 var item = items[++index];
34506 if(item && !item.isHidden()){
34510 // if one isn't found select the previous tab (on the left)
34513 var item = items[--index];
34514 if(item && !item.isHidden()){
34522 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34523 * @param {String/Number} id The id or index of the TabPanelItem to disable.
34525 disableTab : function(id){
34526 var tab = this.items[id];
34527 if(tab && this.active != tab){
34533 * Enables a {@link Roo.TabPanelItem} that is disabled.
34534 * @param {String/Number} id The id or index of the TabPanelItem to enable.
34536 enableTab : function(id){
34537 var tab = this.items[id];
34542 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34543 * @param {String/Number} id The id or index of the TabPanelItem to activate.
34544 * @return {Roo.TabPanelItem} The TabPanelItem.
34546 activate : function(id){
34547 var tab = this.items[id];
34551 if(tab == this.active || tab.disabled){
34555 this.fireEvent("beforetabchange", this, e, tab);
34556 if(e.cancel !== true && !tab.disabled){
34558 this.active.hide();
34560 this.active = this.items[id];
34561 this.active.show();
34562 this.fireEvent("tabchange", this, this.active);
34568 * Gets the active {@link Roo.TabPanelItem}.
34569 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34571 getActiveTab : function(){
34572 return this.active;
34576 * Updates the tab body element to fit the height of the container element
34577 * for overflow scrolling
34578 * @param {Number} targetHeight (optional) Override the starting height from the elements height
34580 syncHeight : function(targetHeight){
34581 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34582 var bm = this.bodyEl.getMargins();
34583 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34584 this.bodyEl.setHeight(newHeight);
34588 onResize : function(){
34589 if(this.monitorResize){
34590 this.autoSizeTabs();
34595 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34597 beginUpdate : function(){
34598 this.updating = true;
34602 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34604 endUpdate : function(){
34605 this.updating = false;
34606 this.autoSizeTabs();
34610 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34612 autoSizeTabs : function(){
34613 var count = this.items.length;
34614 var vcount = count - this.hiddenCount;
34615 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34618 var w = Math.max(this.el.getWidth() - this.cpad, 10);
34619 var availWidth = Math.floor(w / vcount);
34620 var b = this.stripBody;
34621 if(b.getWidth() > w){
34622 var tabs = this.items;
34623 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34624 if(availWidth < this.minTabWidth){
34625 /*if(!this.sleft){ // incomplete scrolling code
34626 this.createScrollButtons();
34629 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34632 if(this.currentTabWidth < this.preferredTabWidth){
34633 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34639 * Returns the number of tabs in this TabPanel.
34642 getCount : function(){
34643 return this.items.length;
34647 * Resizes all the tabs to the passed width
34648 * @param {Number} The new width
34650 setTabWidth : function(width){
34651 this.currentTabWidth = width;
34652 for(var i = 0, len = this.items.length; i < len; i++) {
34653 if(!this.items[i].isHidden()) {
34654 this.items[i].setWidth(width);
34660 * Destroys this TabPanel
34661 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34663 destroy : function(removeEl){
34664 Roo.EventManager.removeResizeListener(this.onResize, this);
34665 for(var i = 0, len = this.items.length; i < len; i++){
34666 this.items[i].purgeListeners();
34668 if(removeEl === true){
34669 this.el.update("");
34674 createStrip : function(container)
34676 var strip = document.createElement("nav");
34677 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34678 container.appendChild(strip);
34682 createStripList : function(strip)
34684 // div wrapper for retard IE
34685 // returns the "tr" element.
34686 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34687 //'<div class="x-tabs-strip-wrap">'+
34688 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34689 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34690 return strip.firstChild; //.firstChild.firstChild.firstChild;
34692 createBody : function(container)
34694 var body = document.createElement("div");
34695 Roo.id(body, "tab-body");
34696 //Roo.fly(body).addClass("x-tabs-body");
34697 Roo.fly(body).addClass("tab-content");
34698 container.appendChild(body);
34701 createItemBody :function(bodyEl, id){
34702 var body = Roo.getDom(id);
34704 body = document.createElement("div");
34707 //Roo.fly(body).addClass("x-tabs-item-body");
34708 Roo.fly(body).addClass("tab-pane");
34709 bodyEl.insertBefore(body, bodyEl.firstChild);
34713 createStripElements : function(stripEl, text, closable)
34715 var td = document.createElement("li"); // was td..
34716 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34717 //stripEl.appendChild(td);
34719 td.className = "x-tabs-closable";
34720 if(!this.closeTpl){
34721 this.closeTpl = new Roo.Template(
34722 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34723 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34724 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
34727 var el = this.closeTpl.overwrite(td, {"text": text});
34728 var close = el.getElementsByTagName("div")[0];
34729 var inner = el.getElementsByTagName("em")[0];
34730 return {"el": el, "close": close, "inner": inner};
34733 // not sure what this is..
34735 //this.tabTpl = new Roo.Template(
34736 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34737 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34739 this.tabTpl = new Roo.Template(
34741 '<span unselectable="on"' +
34742 (this.disableTooltips ? '' : ' title="{text}"') +
34743 ' >{text}</span></span></a>'
34747 var el = this.tabTpl.overwrite(td, {"text": text});
34748 var inner = el.getElementsByTagName("span")[0];
34749 return {"el": el, "inner": inner};
34757 * @class Roo.TabPanelItem
34758 * @extends Roo.util.Observable
34759 * Represents an individual item (tab plus body) in a TabPanel.
34760 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34761 * @param {String} id The id of this TabPanelItem
34762 * @param {String} text The text for the tab of this TabPanelItem
34763 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34765 Roo.bootstrap.panel.TabItem = function(config){
34767 * The {@link Roo.TabPanel} this TabPanelItem belongs to
34768 * @type Roo.TabPanel
34770 this.tabPanel = config.panel;
34772 * The id for this TabPanelItem
34775 this.id = config.id;
34777 this.disabled = false;
34779 this.text = config.text;
34781 this.loaded = false;
34782 this.closable = config.closable;
34785 * The body element for this TabPanelItem.
34786 * @type Roo.Element
34788 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34789 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34790 this.bodyEl.setStyle("display", "block");
34791 this.bodyEl.setStyle("zoom", "1");
34792 //this.hideAction();
34794 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34796 this.el = Roo.get(els.el);
34797 this.inner = Roo.get(els.inner, true);
34798 this.textEl = Roo.get(this.el.dom.firstChild, true);
34799 this.pnode = Roo.get(els.el.parentNode, true);
34800 this.el.on("mousedown", this.onTabMouseDown, this);
34801 this.el.on("click", this.onTabClick, this);
34803 if(config.closable){
34804 var c = Roo.get(els.close, true);
34805 c.dom.title = this.closeText;
34806 c.addClassOnOver("close-over");
34807 c.on("click", this.closeClick, this);
34813 * Fires when this tab becomes the active tab.
34814 * @param {Roo.TabPanel} tabPanel The parent TabPanel
34815 * @param {Roo.TabPanelItem} this
34819 * @event beforeclose
34820 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34821 * @param {Roo.TabPanelItem} this
34822 * @param {Object} e Set cancel to true on this object to cancel the close.
34824 "beforeclose": true,
34827 * Fires when this tab is closed.
34828 * @param {Roo.TabPanelItem} this
34832 * @event deactivate
34833 * Fires when this tab is no longer the active tab.
34834 * @param {Roo.TabPanel} tabPanel The parent TabPanel
34835 * @param {Roo.TabPanelItem} this
34837 "deactivate" : true
34839 this.hidden = false;
34841 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34844 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34846 purgeListeners : function(){
34847 Roo.util.Observable.prototype.purgeListeners.call(this);
34848 this.el.removeAllListeners();
34851 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34854 this.pnode.addClass("active");
34857 this.tabPanel.stripWrap.repaint();
34859 this.fireEvent("activate", this.tabPanel, this);
34863 * Returns true if this tab is the active tab.
34864 * @return {Boolean}
34866 isActive : function(){
34867 return this.tabPanel.getActiveTab() == this;
34871 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34874 this.pnode.removeClass("active");
34876 this.fireEvent("deactivate", this.tabPanel, this);
34879 hideAction : function(){
34880 this.bodyEl.hide();
34881 this.bodyEl.setStyle("position", "absolute");
34882 this.bodyEl.setLeft("-20000px");
34883 this.bodyEl.setTop("-20000px");
34886 showAction : function(){
34887 this.bodyEl.setStyle("position", "relative");
34888 this.bodyEl.setTop("");
34889 this.bodyEl.setLeft("");
34890 this.bodyEl.show();
34894 * Set the tooltip for the tab.
34895 * @param {String} tooltip The tab's tooltip
34897 setTooltip : function(text){
34898 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34899 this.textEl.dom.qtip = text;
34900 this.textEl.dom.removeAttribute('title');
34902 this.textEl.dom.title = text;
34906 onTabClick : function(e){
34907 e.preventDefault();
34908 this.tabPanel.activate(this.id);
34911 onTabMouseDown : function(e){
34912 e.preventDefault();
34913 this.tabPanel.activate(this.id);
34916 getWidth : function(){
34917 return this.inner.getWidth();
34920 setWidth : function(width){
34921 var iwidth = width - this.pnode.getPadding("lr");
34922 this.inner.setWidth(iwidth);
34923 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34924 this.pnode.setWidth(width);
34928 * Show or hide the tab
34929 * @param {Boolean} hidden True to hide or false to show.
34931 setHidden : function(hidden){
34932 this.hidden = hidden;
34933 this.pnode.setStyle("display", hidden ? "none" : "");
34937 * Returns true if this tab is "hidden"
34938 * @return {Boolean}
34940 isHidden : function(){
34941 return this.hidden;
34945 * Returns the text for this tab
34948 getText : function(){
34952 autoSize : function(){
34953 //this.el.beginMeasure();
34954 this.textEl.setWidth(1);
34956 * #2804 [new] Tabs in Roojs
34957 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34959 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34960 //this.el.endMeasure();
34964 * Sets the text for the tab (Note: this also sets the tooltip text)
34965 * @param {String} text The tab's text and tooltip
34967 setText : function(text){
34969 this.textEl.update(text);
34970 this.setTooltip(text);
34971 //if(!this.tabPanel.resizeTabs){
34972 // this.autoSize();
34976 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
34978 activate : function(){
34979 this.tabPanel.activate(this.id);
34983 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
34985 disable : function(){
34986 if(this.tabPanel.active != this){
34987 this.disabled = true;
34988 this.pnode.addClass("disabled");
34993 * Enables this TabPanelItem if it was previously disabled.
34995 enable : function(){
34996 this.disabled = false;
34997 this.pnode.removeClass("disabled");
35001 * Sets the content for this TabPanelItem.
35002 * @param {String} content The content
35003 * @param {Boolean} loadScripts true to look for and load scripts
35005 setContent : function(content, loadScripts){
35006 this.bodyEl.update(content, loadScripts);
35010 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35011 * @return {Roo.UpdateManager} The UpdateManager
35013 getUpdateManager : function(){
35014 return this.bodyEl.getUpdateManager();
35018 * Set a URL to be used to load the content for this TabPanelItem.
35019 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35020 * @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)
35021 * @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)
35022 * @return {Roo.UpdateManager} The UpdateManager
35024 setUrl : function(url, params, loadOnce){
35025 if(this.refreshDelegate){
35026 this.un('activate', this.refreshDelegate);
35028 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35029 this.on("activate", this.refreshDelegate);
35030 return this.bodyEl.getUpdateManager();
35034 _handleRefresh : function(url, params, loadOnce){
35035 if(!loadOnce || !this.loaded){
35036 var updater = this.bodyEl.getUpdateManager();
35037 updater.update(url, params, this._setLoaded.createDelegate(this));
35042 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
35043 * Will fail silently if the setUrl method has not been called.
35044 * This does not activate the panel, just updates its content.
35046 refresh : function(){
35047 if(this.refreshDelegate){
35048 this.loaded = false;
35049 this.refreshDelegate();
35054 _setLoaded : function(){
35055 this.loaded = true;
35059 closeClick : function(e){
35062 this.fireEvent("beforeclose", this, o);
35063 if(o.cancel !== true){
35064 this.tabPanel.removeTab(this.id);
35068 * The text displayed in the tooltip for the close icon.
35071 closeText : "Close this tab"