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').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
2463 * Create a new Modal Dialog
2464 * @param {Object} config The config object
2467 Roo.bootstrap.Modal = function(config){
2468 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2473 * The raw btnclick event for the button
2474 * @param {Roo.EventObject} e
2478 this.buttons = this.buttons || [];
2481 this.tmpl = Roo.factory(this.tmpl);
2486 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2488 title : 'test dialog',
2498 specificTitle: false,
2500 buttonPosition: 'right',
2517 onRender : function(ct, position)
2519 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2522 var cfg = Roo.apply({}, this.getAutoCreate());
2525 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2527 //if (!cfg.name.length) {
2531 cfg.cls += ' ' + this.cls;
2534 cfg.style = this.style;
2536 this.el = Roo.get(document.body).createChild(cfg, position);
2538 //var type = this.el.dom.type;
2541 if(this.tabIndex !== undefined){
2542 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2545 this.dialogEl = this.el.select('.modal-dialog',true).first();
2546 this.bodyEl = this.el.select('.modal-body',true).first();
2547 this.closeEl = this.el.select('.modal-header .close', true).first();
2548 this.footerEl = this.el.select('.modal-footer',true).first();
2549 this.titleEl = this.el.select('.modal-title',true).first();
2553 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2554 this.maskEl.enableDisplayMode("block");
2556 //this.el.addClass("x-dlg-modal");
2558 if (this.buttons.length) {
2559 Roo.each(this.buttons, function(bb) {
2560 var b = Roo.apply({}, bb);
2561 b.xns = b.xns || Roo.bootstrap;
2562 b.xtype = b.xtype || 'Button';
2563 if (typeof(b.listeners) == 'undefined') {
2564 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2567 var btn = Roo.factory(b);
2569 btn.render(this.el.select('.modal-footer div').first());
2573 // render the children.
2576 if(typeof(this.items) != 'undefined'){
2577 var items = this.items;
2580 for(var i =0;i < items.length;i++) {
2581 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2585 this.items = nitems;
2587 // where are these used - they used to be body/close/footer
2591 //this.el.addClass([this.fieldClass, this.cls]);
2595 getAutoCreate : function(){
2600 html : this.html || ''
2605 cls : 'modal-title',
2609 if(this.specificTitle){
2615 if (this.allow_close) {
2626 style : 'display: none',
2629 cls: "modal-dialog",
2632 cls : "modal-content",
2635 cls : 'modal-header',
2640 cls : 'modal-footer',
2644 cls: 'btn-' + this.buttonPosition
2661 modal.cls += ' fade';
2667 getChildContainer : function() {
2672 getButtonContainer : function() {
2673 return this.el.select('.modal-footer div',true).first();
2676 initEvents : function()
2678 if (this.allow_close) {
2679 this.closeEl.on('click', this.hide, this);
2681 Roo.EventManager.onWindowResize(this.resize, this, true);
2688 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2689 if (this.fitwindow) {
2690 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2691 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 30;
2696 setSize : function(w,h)
2706 if (!this.rendered) {
2710 this.el.setStyle('display', 'block');
2712 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2715 this.el.addClass('in');
2718 this.el.addClass('in');
2722 // not sure how we can show data in here..
2724 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2727 Roo.get(document.body).addClass("x-body-masked");
2728 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2730 this.el.setStyle('zIndex', '10001');
2732 this.fireEvent('show', this);
2733 this.items.forEach(function(e) {
2734 e.layout ? e.layout() : false;
2745 Roo.get(document.body).removeClass("x-body-masked");
2746 this.el.removeClass('in');
2747 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2749 if(this.animate){ // why
2751 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2753 this.el.setStyle('display', 'none');
2756 this.fireEvent('hide', this);
2759 addButton : function(str, cb)
2763 var b = Roo.apply({}, { html : str } );
2764 b.xns = b.xns || Roo.bootstrap;
2765 b.xtype = b.xtype || 'Button';
2766 if (typeof(b.listeners) == 'undefined') {
2767 b.listeners = { click : cb.createDelegate(this) };
2770 var btn = Roo.factory(b);
2772 btn.render(this.el.select('.modal-footer div').first());
2778 setDefaultButton : function(btn)
2780 //this.el.select('.modal-footer').()
2784 resizeTo: function(w,h)
2788 this.dialogEl.setWidth(w);
2789 if (this.diff === false) {
2790 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2793 this.bodyEl.setHeight(h-this.diff);
2797 setContentSize : function(w, h)
2801 onButtonClick: function(btn,e)
2804 this.fireEvent('btnclick', btn.name, e);
2807 * Set the title of the Dialog
2808 * @param {String} str new Title
2810 setTitle: function(str) {
2811 this.titleEl.dom.innerHTML = str;
2814 * Set the body of the Dialog
2815 * @param {String} str new Title
2817 setBody: function(str) {
2818 this.bodyEl.dom.innerHTML = str;
2821 * Set the body of the Dialog using the template
2822 * @param {Obj} data - apply this data to the template and replace the body contents.
2824 applyBody: function(obj)
2827 Roo.log("Error - using apply Body without a template");
2830 this.tmpl.overwrite(this.bodyEl, obj);
2836 Roo.apply(Roo.bootstrap.Modal, {
2838 * Button config that displays a single OK button
2847 * Button config that displays Yes and No buttons
2863 * Button config that displays OK and Cancel buttons
2878 * Button config that displays Yes, No and Cancel buttons
2901 * messagebox - can be used as a replace
2905 * @class Roo.MessageBox
2906 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2910 Roo.Msg.alert('Status', 'Changes saved successfully.');
2912 // Prompt for user data:
2913 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2915 // process text value...
2919 // Show a dialog using config options:
2921 title:'Save Changes?',
2922 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2923 buttons: Roo.Msg.YESNOCANCEL,
2930 Roo.bootstrap.MessageBox = function(){
2931 var dlg, opt, mask, waitTimer;
2932 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2933 var buttons, activeTextEl, bwidth;
2937 var handleButton = function(button){
2939 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2943 var handleHide = function(){
2945 dlg.el.removeClass(opt.cls);
2948 // Roo.TaskMgr.stop(waitTimer);
2949 // waitTimer = null;
2954 var updateButtons = function(b){
2957 buttons["ok"].hide();
2958 buttons["cancel"].hide();
2959 buttons["yes"].hide();
2960 buttons["no"].hide();
2961 //dlg.footer.dom.style.display = 'none';
2964 dlg.footerEl.dom.style.display = '';
2965 for(var k in buttons){
2966 if(typeof buttons[k] != "function"){
2969 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2970 width += buttons[k].el.getWidth()+15;
2980 var handleEsc = function(d, k, e){
2981 if(opt && opt.closable !== false){
2991 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2992 * @return {Roo.BasicDialog} The BasicDialog element
2994 getDialog : function(){
2996 dlg = new Roo.bootstrap.Modal( {
2999 //constraintoviewport:false,
3001 //collapsible : false,
3006 //buttonAlign:"center",
3007 closeClick : function(){
3008 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3011 handleButton("cancel");
3016 dlg.on("hide", handleHide);
3018 //dlg.addKeyListener(27, handleEsc);
3020 this.buttons = buttons;
3021 var bt = this.buttonText;
3022 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3023 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3024 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3025 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3027 bodyEl = dlg.bodyEl.createChild({
3029 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3030 '<textarea class="roo-mb-textarea"></textarea>' +
3031 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3033 msgEl = bodyEl.dom.firstChild;
3034 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3035 textboxEl.enableDisplayMode();
3036 textboxEl.addKeyListener([10,13], function(){
3037 if(dlg.isVisible() && opt && opt.buttons){
3040 }else if(opt.buttons.yes){
3041 handleButton("yes");
3045 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3046 textareaEl.enableDisplayMode();
3047 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3048 progressEl.enableDisplayMode();
3049 var pf = progressEl.dom.firstChild;
3051 pp = Roo.get(pf.firstChild);
3052 pp.setHeight(pf.offsetHeight);
3060 * Updates the message box body text
3061 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3062 * the XHTML-compliant non-breaking space character '&#160;')
3063 * @return {Roo.MessageBox} This message box
3065 updateText : function(text){
3066 if(!dlg.isVisible() && !opt.width){
3067 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3069 msgEl.innerHTML = text || ' ';
3071 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3072 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3074 Math.min(opt.width || cw , this.maxWidth),
3075 Math.max(opt.minWidth || this.minWidth, bwidth)
3078 activeTextEl.setWidth(w);
3080 if(dlg.isVisible()){
3081 dlg.fixedcenter = false;
3083 // to big, make it scroll. = But as usual stupid IE does not support
3086 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3087 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3088 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3090 bodyEl.dom.style.height = '';
3091 bodyEl.dom.style.overflowY = '';
3094 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3096 bodyEl.dom.style.overflowX = '';
3099 dlg.setContentSize(w, bodyEl.getHeight());
3100 if(dlg.isVisible()){
3101 dlg.fixedcenter = true;
3107 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3108 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3109 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3110 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3111 * @return {Roo.MessageBox} This message box
3113 updateProgress : function(value, text){
3115 this.updateText(text);
3117 if (pp) { // weird bug on my firefox - for some reason this is not defined
3118 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3124 * Returns true if the message box is currently displayed
3125 * @return {Boolean} True if the message box is visible, else false
3127 isVisible : function(){
3128 return dlg && dlg.isVisible();
3132 * Hides the message box if it is displayed
3135 if(this.isVisible()){
3141 * Displays a new message box, or reinitializes an existing message box, based on the config options
3142 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3143 * The following config object properties are supported:
3145 Property Type Description
3146 ---------- --------------- ------------------------------------------------------------------------------------
3147 animEl String/Element An id or Element from which the message box should animate as it opens and
3148 closes (defaults to undefined)
3149 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3150 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3151 closable Boolean False to hide the top-right close button (defaults to true). Note that
3152 progress and wait dialogs will ignore this property and always hide the
3153 close button as they can only be closed programmatically.
3154 cls String A custom CSS class to apply to the message box element
3155 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3156 displayed (defaults to 75)
3157 fn Function A callback function to execute after closing the dialog. The arguments to the
3158 function will be btn (the name of the button that was clicked, if applicable,
3159 e.g. "ok"), and text (the value of the active text field, if applicable).
3160 Progress and wait dialogs will ignore this option since they do not respond to
3161 user actions and can only be closed programmatically, so any required function
3162 should be called by the same code after it closes the dialog.
3163 icon String A CSS class that provides a background image to be used as an icon for
3164 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3165 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3166 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3167 modal Boolean False to allow user interaction with the page while the message box is
3168 displayed (defaults to true)
3169 msg String A string that will replace the existing message box body text (defaults
3170 to the XHTML-compliant non-breaking space character ' ')
3171 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3172 progress Boolean True to display a progress bar (defaults to false)
3173 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3174 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3175 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3176 title String The title text
3177 value String The string value to set into the active textbox element if displayed
3178 wait Boolean True to display a progress bar (defaults to false)
3179 width Number The width of the dialog in pixels
3186 msg: 'Please enter your address:',
3188 buttons: Roo.MessageBox.OKCANCEL,
3191 animEl: 'addAddressBtn'
3194 * @param {Object} config Configuration options
3195 * @return {Roo.MessageBox} This message box
3197 show : function(options)
3200 // this causes nightmares if you show one dialog after another
3201 // especially on callbacks..
3203 if(this.isVisible()){
3206 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3207 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3208 Roo.log("New Dialog Message:" + options.msg )
3209 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3210 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3213 var d = this.getDialog();
3215 d.setTitle(opt.title || " ");
3216 d.closeEl.setDisplayed(opt.closable !== false);
3217 activeTextEl = textboxEl;
3218 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3223 textareaEl.setHeight(typeof opt.multiline == "number" ?
3224 opt.multiline : this.defaultTextHeight);
3225 activeTextEl = textareaEl;
3234 progressEl.setDisplayed(opt.progress === true);
3235 this.updateProgress(0);
3236 activeTextEl.dom.value = opt.value || "";
3238 dlg.setDefaultButton(activeTextEl);
3240 var bs = opt.buttons;
3244 }else if(bs && bs.yes){
3245 db = buttons["yes"];
3247 dlg.setDefaultButton(db);
3249 bwidth = updateButtons(opt.buttons);
3250 this.updateText(opt.msg);
3252 d.el.addClass(opt.cls);
3254 d.proxyDrag = opt.proxyDrag === true;
3255 d.modal = opt.modal !== false;
3256 d.mask = opt.modal !== false ? mask : false;
3258 // force it to the end of the z-index stack so it gets a cursor in FF
3259 document.body.appendChild(dlg.el.dom);
3260 d.animateTarget = null;
3261 d.show(options.animEl);
3267 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3268 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3269 * and closing the message box when the process is complete.
3270 * @param {String} title The title bar text
3271 * @param {String} msg The message box body text
3272 * @return {Roo.MessageBox} This message box
3274 progress : function(title, msg){
3281 minWidth: this.minProgressWidth,
3288 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3289 * If a callback function is passed it will be called after the user clicks the button, and the
3290 * id of the button that was clicked will be passed as the only parameter to the callback
3291 * (could also be the top-right close button).
3292 * @param {String} title The title bar text
3293 * @param {String} msg The message box body text
3294 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3295 * @param {Object} scope (optional) The scope of the callback function
3296 * @return {Roo.MessageBox} This message box
3298 alert : function(title, msg, fn, scope){
3311 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3312 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3313 * You are responsible for closing the message box when the process is complete.
3314 * @param {String} msg The message box body text
3315 * @param {String} title (optional) The title bar text
3316 * @return {Roo.MessageBox} This message box
3318 wait : function(msg, title){
3329 waitTimer = Roo.TaskMgr.start({
3331 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3339 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3340 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3341 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3342 * @param {String} title The title bar text
3343 * @param {String} msg The message box body text
3344 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3345 * @param {Object} scope (optional) The scope of the callback function
3346 * @return {Roo.MessageBox} This message box
3348 confirm : function(title, msg, fn, scope){
3352 buttons: this.YESNO,
3361 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3362 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3363 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3364 * (could also be the top-right close button) and the text that was entered will be passed as the two
3365 * parameters to the callback.
3366 * @param {String} title The title bar text
3367 * @param {String} msg The message box body text
3368 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3369 * @param {Object} scope (optional) The scope of the callback function
3370 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3371 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3372 * @return {Roo.MessageBox} This message box
3374 prompt : function(title, msg, fn, scope, multiline){
3378 buttons: this.OKCANCEL,
3383 multiline: multiline,
3390 * Button config that displays a single OK button
3395 * Button config that displays Yes and No buttons
3398 YESNO : {yes:true, no:true},
3400 * Button config that displays OK and Cancel buttons
3403 OKCANCEL : {ok:true, cancel:true},
3405 * Button config that displays Yes, No and Cancel buttons
3408 YESNOCANCEL : {yes:true, no:true, cancel:true},
3411 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3414 defaultTextHeight : 75,
3416 * The maximum width in pixels of the message box (defaults to 600)
3421 * The minimum width in pixels of the message box (defaults to 100)
3426 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3427 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3430 minProgressWidth : 250,
3432 * An object containing the default button text strings that can be overriden for localized language support.
3433 * Supported properties are: ok, cancel, yes and no.
3434 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3447 * Shorthand for {@link Roo.MessageBox}
3449 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3450 Roo.Msg = Roo.Msg || Roo.MessageBox;
3459 * @class Roo.bootstrap.Navbar
3460 * @extends Roo.bootstrap.Component
3461 * Bootstrap Navbar class
3464 * Create a new Navbar
3465 * @param {Object} config The config object
3469 Roo.bootstrap.Navbar = function(config){
3470 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3474 * @event beforetoggle
3475 * Fire before toggle the menu
3476 * @param {Roo.EventObject} e
3478 "beforetoggle" : true
3482 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3491 getAutoCreate : function(){
3494 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3498 initEvents :function ()
3500 //Roo.log(this.el.select('.navbar-toggle',true));
3501 this.el.select('.navbar-toggle',true).on('click', function() {
3502 if(this.fireEvent('beforetoggle', this) !== false){
3503 this.el.select('.navbar-collapse',true).toggleClass('in');
3513 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3515 var size = this.el.getSize();
3516 this.maskEl.setSize(size.width, size.height);
3517 this.maskEl.enableDisplayMode("block");
3526 getChildContainer : function()
3528 if (this.el.select('.collapse').getCount()) {
3529 return this.el.select('.collapse',true).first();
3562 * @class Roo.bootstrap.NavSimplebar
3563 * @extends Roo.bootstrap.Navbar
3564 * Bootstrap Sidebar class
3566 * @cfg {Boolean} inverse is inverted color
3568 * @cfg {String} type (nav | pills | tabs)
3569 * @cfg {Boolean} arrangement stacked | justified
3570 * @cfg {String} align (left | right) alignment
3572 * @cfg {Boolean} main (true|false) main nav bar? default false
3573 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3575 * @cfg {String} tag (header|footer|nav|div) default is nav
3581 * Create a new Sidebar
3582 * @param {Object} config The config object
3586 Roo.bootstrap.NavSimplebar = function(config){
3587 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3590 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3606 getAutoCreate : function(){
3610 tag : this.tag || 'div',
3623 this.type = this.type || 'nav';
3624 if (['tabs','pills'].indexOf(this.type)!==-1) {
3625 cfg.cn[0].cls += ' nav-' + this.type
3629 if (this.type!=='nav') {
3630 Roo.log('nav type must be nav/tabs/pills')
3632 cfg.cn[0].cls += ' navbar-nav'
3638 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3639 cfg.cn[0].cls += ' nav-' + this.arrangement;
3643 if (this.align === 'right') {
3644 cfg.cn[0].cls += ' navbar-right';
3648 cfg.cls += ' navbar-inverse';
3675 * @class Roo.bootstrap.NavHeaderbar
3676 * @extends Roo.bootstrap.NavSimplebar
3677 * Bootstrap Sidebar class
3679 * @cfg {String} brand what is brand
3680 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3681 * @cfg {String} brand_href href of the brand
3682 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3683 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3684 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3685 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3688 * Create a new Sidebar
3689 * @param {Object} config The config object
3693 Roo.bootstrap.NavHeaderbar = function(config){
3694 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3698 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3705 desktopCenter : false,
3708 getAutoCreate : function(){
3711 tag: this.nav || 'nav',
3718 if (this.desktopCenter) {
3719 cn.push({cls : 'container', cn : []});
3726 cls: 'navbar-header',
3731 cls: 'navbar-toggle',
3732 'data-toggle': 'collapse',
3737 html: 'Toggle navigation'
3759 cls: 'collapse navbar-collapse',
3763 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3765 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3766 cfg.cls += ' navbar-' + this.position;
3768 // tag can override this..
3770 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3773 if (this.brand !== '') {
3776 href: this.brand_href ? this.brand_href : '#',
3777 cls: 'navbar-brand',
3785 cfg.cls += ' main-nav';
3793 getHeaderChildContainer : function()
3795 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3796 return this.el.select('.navbar-header',true).first();
3799 return this.getChildContainer();
3803 initEvents : function()
3805 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3807 if (this.autohide) {
3812 Roo.get(document).on('scroll',function(e) {
3813 var ns = Roo.get(document).getScroll().top;
3814 var os = prevScroll;
3818 ft.removeClass('slideDown');
3819 ft.addClass('slideUp');
3822 ft.removeClass('slideUp');
3823 ft.addClass('slideDown');
3844 * @class Roo.bootstrap.NavSidebar
3845 * @extends Roo.bootstrap.Navbar
3846 * Bootstrap Sidebar class
3849 * Create a new Sidebar
3850 * @param {Object} config The config object
3854 Roo.bootstrap.NavSidebar = function(config){
3855 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3858 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3860 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3862 getAutoCreate : function(){
3867 cls: 'sidebar sidebar-nav'
3889 * @class Roo.bootstrap.NavGroup
3890 * @extends Roo.bootstrap.Component
3891 * Bootstrap NavGroup class
3892 * @cfg {String} align (left|right)
3893 * @cfg {Boolean} inverse
3894 * @cfg {String} type (nav|pills|tab) default nav
3895 * @cfg {String} navId - reference Id for navbar.
3899 * Create a new nav group
3900 * @param {Object} config The config object
3903 Roo.bootstrap.NavGroup = function(config){
3904 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3907 Roo.bootstrap.NavGroup.register(this);
3911 * Fires when the active item changes
3912 * @param {Roo.bootstrap.NavGroup} this
3913 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3914 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3921 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3932 getAutoCreate : function()
3934 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3941 if (['tabs','pills'].indexOf(this.type)!==-1) {
3942 cfg.cls += ' nav-' + this.type
3944 if (this.type!=='nav') {
3945 Roo.log('nav type must be nav/tabs/pills')
3947 cfg.cls += ' navbar-nav'
3950 if (this.parent().sidebar) {
3953 cls: 'dashboard-menu sidebar-menu'
3959 if (this.form === true) {
3965 if (this.align === 'right') {
3966 cfg.cls += ' navbar-right';
3968 cfg.cls += ' navbar-left';
3972 if (this.align === 'right') {
3973 cfg.cls += ' navbar-right';
3977 cfg.cls += ' navbar-inverse';
3985 * sets the active Navigation item
3986 * @param {Roo.bootstrap.NavItem} the new current navitem
3988 setActiveItem : function(item)
3991 Roo.each(this.navItems, function(v){
3996 v.setActive(false, true);
4003 item.setActive(true, true);
4004 this.fireEvent('changed', this, item, prev);
4009 * gets the active Navigation item
4010 * @return {Roo.bootstrap.NavItem} the current navitem
4012 getActive : function()
4016 Roo.each(this.navItems, function(v){
4027 indexOfNav : function()
4031 Roo.each(this.navItems, function(v,i){
4042 * adds a Navigation item
4043 * @param {Roo.bootstrap.NavItem} the navitem to add
4045 addItem : function(cfg)
4047 var cn = new Roo.bootstrap.NavItem(cfg);
4049 cn.parentId = this.id;
4050 cn.onRender(this.el, null);
4054 * register a Navigation item
4055 * @param {Roo.bootstrap.NavItem} the navitem to add
4057 register : function(item)
4059 this.navItems.push( item);
4060 item.navId = this.navId;
4065 * clear all the Navigation item
4068 clearAll : function()
4071 this.el.dom.innerHTML = '';
4074 getNavItem: function(tabId)
4077 Roo.each(this.navItems, function(e) {
4078 if (e.tabId == tabId) {
4088 setActiveNext : function()
4090 var i = this.indexOfNav(this.getActive());
4091 if (i > this.navItems.length) {
4094 this.setActiveItem(this.navItems[i+1]);
4096 setActivePrev : function()
4098 var i = this.indexOfNav(this.getActive());
4102 this.setActiveItem(this.navItems[i-1]);
4104 clearWasActive : function(except) {
4105 Roo.each(this.navItems, function(e) {
4106 if (e.tabId != except.tabId && e.was_active) {
4107 e.was_active = false;
4114 getWasActive : function ()
4117 Roo.each(this.navItems, function(e) {
4132 Roo.apply(Roo.bootstrap.NavGroup, {
4136 * register a Navigation Group
4137 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4139 register : function(navgrp)
4141 this.groups[navgrp.navId] = navgrp;
4145 * fetch a Navigation Group based on the navigation ID
4146 * @param {string} the navgroup to add
4147 * @returns {Roo.bootstrap.NavGroup} the navgroup
4149 get: function(navId) {
4150 if (typeof(this.groups[navId]) == 'undefined') {
4152 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4154 return this.groups[navId] ;
4169 * @class Roo.bootstrap.NavItem
4170 * @extends Roo.bootstrap.Component
4171 * Bootstrap Navbar.NavItem class
4172 * @cfg {String} href link to
4173 * @cfg {String} html content of button
4174 * @cfg {String} badge text inside badge
4175 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4176 * @cfg {String} glyphicon name of glyphicon
4177 * @cfg {String} icon name of font awesome icon
4178 * @cfg {Boolean} active Is item active
4179 * @cfg {Boolean} disabled Is item disabled
4181 * @cfg {Boolean} preventDefault (true | false) default false
4182 * @cfg {String} tabId the tab that this item activates.
4183 * @cfg {String} tagtype (a|span) render as a href or span?
4184 * @cfg {Boolean} animateRef (true|false) link to element default false
4187 * Create a new Navbar Item
4188 * @param {Object} config The config object
4190 Roo.bootstrap.NavItem = function(config){
4191 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4196 * The raw click event for the entire grid.
4197 * @param {Roo.EventObject} e
4202 * Fires when the active item active state changes
4203 * @param {Roo.bootstrap.NavItem} this
4204 * @param {boolean} state the new state
4210 * Fires when scroll to element
4211 * @param {Roo.bootstrap.NavItem} this
4212 * @param {Object} options
4213 * @param {Roo.EventObject} e
4221 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4229 preventDefault : false,
4236 getAutoCreate : function(){
4245 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4247 if (this.disabled) {
4248 cfg.cls += ' disabled';
4251 if (this.href || this.html || this.glyphicon || this.icon) {
4255 href : this.href || "#",
4256 html: this.html || ''
4261 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4264 if(this.glyphicon) {
4265 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4270 cfg.cn[0].html += " <span class='caret'></span>";
4274 if (this.badge !== '') {
4276 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4284 initEvents: function()
4286 if (typeof (this.menu) != 'undefined') {
4287 this.menu.parentType = this.xtype;
4288 this.menu.triggerEl = this.el;
4289 this.menu = this.addxtype(Roo.apply({}, this.menu));
4292 this.el.select('a',true).on('click', this.onClick, this);
4294 if(this.tagtype == 'span'){
4295 this.el.select('span',true).on('click', this.onClick, this);
4298 // at this point parent should be available..
4299 this.parent().register(this);
4302 onClick : function(e)
4304 if (e.getTarget('.dropdown-menu-item')) {
4305 // did you click on a menu itemm.... - then don't trigger onclick..
4310 this.preventDefault ||
4313 Roo.log("NavItem - prevent Default?");
4317 if (this.disabled) {
4321 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4322 if (tg && tg.transition) {
4323 Roo.log("waiting for the transitionend");
4329 //Roo.log("fire event clicked");
4330 if(this.fireEvent('click', this, e) === false){
4334 if(this.tagtype == 'span'){
4338 //Roo.log(this.href);
4339 var ael = this.el.select('a',true).first();
4342 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4343 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4344 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4345 return; // ignore... - it's a 'hash' to another page.
4347 Roo.log("NavItem - prevent Default?");
4349 this.scrollToElement(e);
4353 var p = this.parent();
4355 if (['tabs','pills'].indexOf(p.type)!==-1) {
4356 if (typeof(p.setActiveItem) !== 'undefined') {
4357 p.setActiveItem(this);
4361 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4362 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4363 // remove the collapsed menu expand...
4364 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4368 isActive: function () {
4371 setActive : function(state, fire, is_was_active)
4373 if (this.active && !state && this.navId) {
4374 this.was_active = true;
4375 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4377 nv.clearWasActive(this);
4381 this.active = state;
4384 this.el.removeClass('active');
4385 } else if (!this.el.hasClass('active')) {
4386 this.el.addClass('active');
4389 this.fireEvent('changed', this, state);
4392 // show a panel if it's registered and related..
4394 if (!this.navId || !this.tabId || !state || is_was_active) {
4398 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4402 var pan = tg.getPanelByName(this.tabId);
4406 // if we can not flip to new panel - go back to old nav highlight..
4407 if (false == tg.showPanel(pan)) {
4408 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4410 var onav = nv.getWasActive();
4412 onav.setActive(true, false, true);
4421 // this should not be here...
4422 setDisabled : function(state)
4424 this.disabled = state;
4426 this.el.removeClass('disabled');
4427 } else if (!this.el.hasClass('disabled')) {
4428 this.el.addClass('disabled');
4434 * Fetch the element to display the tooltip on.
4435 * @return {Roo.Element} defaults to this.el
4437 tooltipEl : function()
4439 return this.el.select('' + this.tagtype + '', true).first();
4442 scrollToElement : function(e)
4444 var c = document.body;
4447 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4449 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4450 c = document.documentElement;
4453 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4459 var o = target.calcOffsetsTo(c);
4466 this.fireEvent('scrollto', this, options, e);
4468 Roo.get(c).scrollTo('top', options.value, true);
4481 * <span> icon </span>
4482 * <span> text </span>
4483 * <span>badge </span>
4487 * @class Roo.bootstrap.NavSidebarItem
4488 * @extends Roo.bootstrap.NavItem
4489 * Bootstrap Navbar.NavSidebarItem class
4490 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4491 * {bool} open is the menu open
4493 * Create a new Navbar Button
4494 * @param {Object} config The config object
4496 Roo.bootstrap.NavSidebarItem = function(config){
4497 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4502 * The raw click event for the entire grid.
4503 * @param {Roo.EventObject} e
4508 * Fires when the active item active state changes
4509 * @param {Roo.bootstrap.NavSidebarItem} this
4510 * @param {boolean} state the new state
4518 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4520 badgeWeight : 'default',
4524 getAutoCreate : function(){
4529 href : this.href || '#',
4541 html : this.html || ''
4546 cfg.cls += ' active';
4549 if (this.disabled) {
4550 cfg.cls += ' disabled';
4553 cfg.cls += ' open x-open';
4556 if (this.glyphicon || this.icon) {
4557 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4558 a.cn.push({ tag : 'i', cls : c }) ;
4563 if (this.badge !== '') {
4565 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4569 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4570 a.cls += 'dropdown-toggle treeview' ;
4578 initEvents : function()
4580 if (typeof (this.menu) != 'undefined') {
4581 this.menu.parentType = this.xtype;
4582 this.menu.triggerEl = this.el;
4583 this.menu = this.addxtype(Roo.apply({}, this.menu));
4586 this.el.on('click', this.onClick, this);
4589 if(this.badge !== ''){
4591 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4596 onClick : function(e)
4603 if(this.preventDefault){
4607 this.fireEvent('click', this);
4610 disable : function()
4612 this.setDisabled(true);
4617 this.setDisabled(false);
4620 setDisabled : function(state)
4622 if(this.disabled == state){
4626 this.disabled = state;
4629 this.el.addClass('disabled');
4633 this.el.removeClass('disabled');
4638 setActive : function(state)
4640 if(this.active == state){
4644 this.active = state;
4647 this.el.addClass('active');
4651 this.el.removeClass('active');
4656 isActive: function ()
4661 setBadge : function(str)
4667 this.badgeEl.dom.innerHTML = str;
4684 * @class Roo.bootstrap.Row
4685 * @extends Roo.bootstrap.Component
4686 * Bootstrap Row class (contains columns...)
4690 * @param {Object} config The config object
4693 Roo.bootstrap.Row = function(config){
4694 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4697 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4699 getAutoCreate : function(){
4718 * @class Roo.bootstrap.Element
4719 * @extends Roo.bootstrap.Component
4720 * Bootstrap Element class
4721 * @cfg {String} html contents of the element
4722 * @cfg {String} tag tag of the element
4723 * @cfg {String} cls class of the element
4724 * @cfg {Boolean} preventDefault (true|false) default false
4725 * @cfg {Boolean} clickable (true|false) default false
4728 * Create a new Element
4729 * @param {Object} config The config object
4732 Roo.bootstrap.Element = function(config){
4733 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4739 * When a element is chick
4740 * @param {Roo.bootstrap.Element} this
4741 * @param {Roo.EventObject} e
4747 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4752 preventDefault: false,
4755 getAutoCreate : function(){
4766 initEvents: function()
4768 Roo.bootstrap.Element.superclass.initEvents.call(this);
4771 this.el.on('click', this.onClick, this);
4776 onClick : function(e)
4778 if(this.preventDefault){
4782 this.fireEvent('click', this, e);
4785 getValue : function()
4787 return this.el.dom.innerHTML;
4790 setValue : function(value)
4792 this.el.dom.innerHTML = value;
4807 * @class Roo.bootstrap.Pagination
4808 * @extends Roo.bootstrap.Component
4809 * Bootstrap Pagination class
4810 * @cfg {String} size xs | sm | md | lg
4811 * @cfg {Boolean} inverse false | true
4814 * Create a new Pagination
4815 * @param {Object} config The config object
4818 Roo.bootstrap.Pagination = function(config){
4819 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4822 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4828 getAutoCreate : function(){
4834 cfg.cls += ' inverse';
4840 cfg.cls += " " + this.cls;
4858 * @class Roo.bootstrap.PaginationItem
4859 * @extends Roo.bootstrap.Component
4860 * Bootstrap PaginationItem class
4861 * @cfg {String} html text
4862 * @cfg {String} href the link
4863 * @cfg {Boolean} preventDefault (true | false) default true
4864 * @cfg {Boolean} active (true | false) default false
4865 * @cfg {Boolean} disabled default false
4869 * Create a new PaginationItem
4870 * @param {Object} config The config object
4874 Roo.bootstrap.PaginationItem = function(config){
4875 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4880 * The raw click event for the entire grid.
4881 * @param {Roo.EventObject} e
4887 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4891 preventDefault: true,
4896 getAutoCreate : function(){
4902 href : this.href ? this.href : '#',
4903 html : this.html ? this.html : ''
4913 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4917 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4923 initEvents: function() {
4925 this.el.on('click', this.onClick, this);
4928 onClick : function(e)
4930 Roo.log('PaginationItem on click ');
4931 if(this.preventDefault){
4939 this.fireEvent('click', this, e);
4955 * @class Roo.bootstrap.Slider
4956 * @extends Roo.bootstrap.Component
4957 * Bootstrap Slider class
4960 * Create a new Slider
4961 * @param {Object} config The config object
4964 Roo.bootstrap.Slider = function(config){
4965 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4968 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4970 getAutoCreate : function(){
4974 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4978 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4990 * Ext JS Library 1.1.1
4991 * Copyright(c) 2006-2007, Ext JS, LLC.
4993 * Originally Released Under LGPL - original licence link has changed is not relivant.
4996 * <script type="text/javascript">
5001 * @class Roo.grid.ColumnModel
5002 * @extends Roo.util.Observable
5003 * This is the default implementation of a ColumnModel used by the Grid. It defines
5004 * the columns in the grid.
5007 var colModel = new Roo.grid.ColumnModel([
5008 {header: "Ticker", width: 60, sortable: true, locked: true},
5009 {header: "Company Name", width: 150, sortable: true},
5010 {header: "Market Cap.", width: 100, sortable: true},
5011 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5012 {header: "Employees", width: 100, sortable: true, resizable: false}
5017 * The config options listed for this class are options which may appear in each
5018 * individual column definition.
5019 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5021 * @param {Object} config An Array of column config objects. See this class's
5022 * config objects for details.
5024 Roo.grid.ColumnModel = function(config){
5026 * The config passed into the constructor
5028 this.config = config;
5031 // if no id, create one
5032 // if the column does not have a dataIndex mapping,
5033 // map it to the order it is in the config
5034 for(var i = 0, len = config.length; i < len; i++){
5036 if(typeof c.dataIndex == "undefined"){
5039 if(typeof c.renderer == "string"){
5040 c.renderer = Roo.util.Format[c.renderer];
5042 if(typeof c.id == "undefined"){
5045 if(c.editor && c.editor.xtype){
5046 c.editor = Roo.factory(c.editor, Roo.grid);
5048 if(c.editor && c.editor.isFormField){
5049 c.editor = new Roo.grid.GridEditor(c.editor);
5051 this.lookup[c.id] = c;
5055 * The width of columns which have no width specified (defaults to 100)
5058 this.defaultWidth = 100;
5061 * Default sortable of columns which have no sortable specified (defaults to false)
5064 this.defaultSortable = false;
5068 * @event widthchange
5069 * Fires when the width of a column changes.
5070 * @param {ColumnModel} this
5071 * @param {Number} columnIndex The column index
5072 * @param {Number} newWidth The new width
5074 "widthchange": true,
5076 * @event headerchange
5077 * Fires when the text of a header changes.
5078 * @param {ColumnModel} this
5079 * @param {Number} columnIndex The column index
5080 * @param {Number} newText The new header text
5082 "headerchange": true,
5084 * @event hiddenchange
5085 * Fires when a column is hidden or "unhidden".
5086 * @param {ColumnModel} this
5087 * @param {Number} columnIndex The column index
5088 * @param {Boolean} hidden true if hidden, false otherwise
5090 "hiddenchange": true,
5092 * @event columnmoved
5093 * Fires when a column is moved.
5094 * @param {ColumnModel} this
5095 * @param {Number} oldIndex
5096 * @param {Number} newIndex
5098 "columnmoved" : true,
5100 * @event columlockchange
5101 * Fires when a column's locked state is changed
5102 * @param {ColumnModel} this
5103 * @param {Number} colIndex
5104 * @param {Boolean} locked true if locked
5106 "columnlockchange" : true
5108 Roo.grid.ColumnModel.superclass.constructor.call(this);
5110 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5112 * @cfg {String} header The header text to display in the Grid view.
5115 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5116 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5117 * specified, the column's index is used as an index into the Record's data Array.
5120 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5121 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5124 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5125 * Defaults to the value of the {@link #defaultSortable} property.
5126 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5129 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5132 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5135 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5138 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5141 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5142 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5143 * default renderer uses the raw data value. If an object is returned (bootstrap only)
5144 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5147 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5150 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5153 * @cfg {String} cursor (Optional)
5156 * @cfg {String} tooltip (Optional)
5159 * @cfg {Number} xs (Optional)
5162 * @cfg {Number} sm (Optional)
5165 * @cfg {Number} md (Optional)
5168 * @cfg {Number} lg (Optional)
5171 * Returns the id of the column at the specified index.
5172 * @param {Number} index The column index
5173 * @return {String} the id
5175 getColumnId : function(index){
5176 return this.config[index].id;
5180 * Returns the column for a specified id.
5181 * @param {String} id The column id
5182 * @return {Object} the column
5184 getColumnById : function(id){
5185 return this.lookup[id];
5190 * Returns the column for a specified dataIndex.
5191 * @param {String} dataIndex The column dataIndex
5192 * @return {Object|Boolean} the column or false if not found
5194 getColumnByDataIndex: function(dataIndex){
5195 var index = this.findColumnIndex(dataIndex);
5196 return index > -1 ? this.config[index] : false;
5200 * Returns the index for a specified column id.
5201 * @param {String} id The column id
5202 * @return {Number} the index, or -1 if not found
5204 getIndexById : function(id){
5205 for(var i = 0, len = this.config.length; i < len; i++){
5206 if(this.config[i].id == id){
5214 * Returns the index for a specified column dataIndex.
5215 * @param {String} dataIndex The column dataIndex
5216 * @return {Number} the index, or -1 if not found
5219 findColumnIndex : function(dataIndex){
5220 for(var i = 0, len = this.config.length; i < len; i++){
5221 if(this.config[i].dataIndex == dataIndex){
5229 moveColumn : function(oldIndex, newIndex){
5230 var c = this.config[oldIndex];
5231 this.config.splice(oldIndex, 1);
5232 this.config.splice(newIndex, 0, c);
5233 this.dataMap = null;
5234 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5237 isLocked : function(colIndex){
5238 return this.config[colIndex].locked === true;
5241 setLocked : function(colIndex, value, suppressEvent){
5242 if(this.isLocked(colIndex) == value){
5245 this.config[colIndex].locked = value;
5247 this.fireEvent("columnlockchange", this, colIndex, value);
5251 getTotalLockedWidth : function(){
5253 for(var i = 0; i < this.config.length; i++){
5254 if(this.isLocked(i) && !this.isHidden(i)){
5255 this.totalWidth += this.getColumnWidth(i);
5261 getLockedCount : function(){
5262 for(var i = 0, len = this.config.length; i < len; i++){
5263 if(!this.isLocked(i)){
5268 return this.config.length;
5272 * Returns the number of columns.
5275 getColumnCount : function(visibleOnly){
5276 if(visibleOnly === true){
5278 for(var i = 0, len = this.config.length; i < len; i++){
5279 if(!this.isHidden(i)){
5285 return this.config.length;
5289 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5290 * @param {Function} fn
5291 * @param {Object} scope (optional)
5292 * @return {Array} result
5294 getColumnsBy : function(fn, scope){
5296 for(var i = 0, len = this.config.length; i < len; i++){
5297 var c = this.config[i];
5298 if(fn.call(scope||this, c, i) === true){
5306 * Returns true if the specified column is sortable.
5307 * @param {Number} col The column index
5310 isSortable : function(col){
5311 if(typeof this.config[col].sortable == "undefined"){
5312 return this.defaultSortable;
5314 return this.config[col].sortable;
5318 * Returns the rendering (formatting) function defined for the column.
5319 * @param {Number} col The column index.
5320 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5322 getRenderer : function(col){
5323 if(!this.config[col].renderer){
5324 return Roo.grid.ColumnModel.defaultRenderer;
5326 return this.config[col].renderer;
5330 * Sets the rendering (formatting) function for a column.
5331 * @param {Number} col The column index
5332 * @param {Function} fn The function to use to process the cell's raw data
5333 * to return HTML markup for the grid view. The render function is called with
5334 * the following parameters:<ul>
5335 * <li>Data value.</li>
5336 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5337 * <li>css A CSS style string to apply to the table cell.</li>
5338 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5339 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5340 * <li>Row index</li>
5341 * <li>Column index</li>
5342 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5344 setRenderer : function(col, fn){
5345 this.config[col].renderer = fn;
5349 * Returns the width for the specified column.
5350 * @param {Number} col The column index
5353 getColumnWidth : function(col){
5354 return this.config[col].width * 1 || this.defaultWidth;
5358 * Sets the width for a column.
5359 * @param {Number} col The column index
5360 * @param {Number} width The new width
5362 setColumnWidth : function(col, width, suppressEvent){
5363 this.config[col].width = width;
5364 this.totalWidth = null;
5366 this.fireEvent("widthchange", this, col, width);
5371 * Returns the total width of all columns.
5372 * @param {Boolean} includeHidden True to include hidden column widths
5375 getTotalWidth : function(includeHidden){
5376 if(!this.totalWidth){
5377 this.totalWidth = 0;
5378 for(var i = 0, len = this.config.length; i < len; i++){
5379 if(includeHidden || !this.isHidden(i)){
5380 this.totalWidth += this.getColumnWidth(i);
5384 return this.totalWidth;
5388 * Returns the header for the specified column.
5389 * @param {Number} col The column index
5392 getColumnHeader : function(col){
5393 return this.config[col].header;
5397 * Sets the header for a column.
5398 * @param {Number} col The column index
5399 * @param {String} header The new header
5401 setColumnHeader : function(col, header){
5402 this.config[col].header = header;
5403 this.fireEvent("headerchange", this, col, header);
5407 * Returns the tooltip for the specified column.
5408 * @param {Number} col The column index
5411 getColumnTooltip : function(col){
5412 return this.config[col].tooltip;
5415 * Sets the tooltip for a column.
5416 * @param {Number} col The column index
5417 * @param {String} tooltip The new tooltip
5419 setColumnTooltip : function(col, tooltip){
5420 this.config[col].tooltip = tooltip;
5424 * Returns the dataIndex for the specified column.
5425 * @param {Number} col The column index
5428 getDataIndex : function(col){
5429 return this.config[col].dataIndex;
5433 * Sets the dataIndex for a column.
5434 * @param {Number} col The column index
5435 * @param {Number} dataIndex The new dataIndex
5437 setDataIndex : function(col, dataIndex){
5438 this.config[col].dataIndex = dataIndex;
5444 * Returns true if the cell is editable.
5445 * @param {Number} colIndex The column index
5446 * @param {Number} rowIndex The row index - this is nto actually used..?
5449 isCellEditable : function(colIndex, rowIndex){
5450 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5454 * Returns the editor defined for the cell/column.
5455 * return false or null to disable editing.
5456 * @param {Number} colIndex The column index
5457 * @param {Number} rowIndex The row index
5460 getCellEditor : function(colIndex, rowIndex){
5461 return this.config[colIndex].editor;
5465 * Sets if a column is editable.
5466 * @param {Number} col The column index
5467 * @param {Boolean} editable True if the column is editable
5469 setEditable : function(col, editable){
5470 this.config[col].editable = editable;
5475 * Returns true if the column is hidden.
5476 * @param {Number} colIndex The column index
5479 isHidden : function(colIndex){
5480 return this.config[colIndex].hidden;
5485 * Returns true if the column width cannot be changed
5487 isFixed : function(colIndex){
5488 return this.config[colIndex].fixed;
5492 * Returns true if the column can be resized
5495 isResizable : function(colIndex){
5496 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5499 * Sets if a column is hidden.
5500 * @param {Number} colIndex The column index
5501 * @param {Boolean} hidden True if the column is hidden
5503 setHidden : function(colIndex, hidden){
5504 this.config[colIndex].hidden = hidden;
5505 this.totalWidth = null;
5506 this.fireEvent("hiddenchange", this, colIndex, hidden);
5510 * Sets the editor for a column.
5511 * @param {Number} col The column index
5512 * @param {Object} editor The editor object
5514 setEditor : function(col, editor){
5515 this.config[col].editor = editor;
5519 Roo.grid.ColumnModel.defaultRenderer = function(value){
5520 if(typeof value == "string" && value.length < 1){
5526 // Alias for backwards compatibility
5527 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5530 * Ext JS Library 1.1.1
5531 * Copyright(c) 2006-2007, Ext JS, LLC.
5533 * Originally Released Under LGPL - original licence link has changed is not relivant.
5536 * <script type="text/javascript">
5540 * @class Roo.LoadMask
5541 * A simple utility class for generically masking elements while loading data. If the element being masked has
5542 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5543 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5544 * element's UpdateManager load indicator and will be destroyed after the initial load.
5546 * Create a new LoadMask
5547 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5548 * @param {Object} config The config object
5550 Roo.LoadMask = function(el, config){
5551 this.el = Roo.get(el);
5552 Roo.apply(this, config);
5554 this.store.on('beforeload', this.onBeforeLoad, this);
5555 this.store.on('load', this.onLoad, this);
5556 this.store.on('loadexception', this.onLoadException, this);
5557 this.removeMask = false;
5559 var um = this.el.getUpdateManager();
5560 um.showLoadIndicator = false; // disable the default indicator
5561 um.on('beforeupdate', this.onBeforeLoad, this);
5562 um.on('update', this.onLoad, this);
5563 um.on('failure', this.onLoad, this);
5564 this.removeMask = true;
5568 Roo.LoadMask.prototype = {
5570 * @cfg {Boolean} removeMask
5571 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5572 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5576 * The text to display in a centered loading message box (defaults to 'Loading...')
5580 * @cfg {String} msgCls
5581 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5583 msgCls : 'x-mask-loading',
5586 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5592 * Disables the mask to prevent it from being displayed
5594 disable : function(){
5595 this.disabled = true;
5599 * Enables the mask so that it can be displayed
5601 enable : function(){
5602 this.disabled = false;
5605 onLoadException : function()
5609 if (typeof(arguments[3]) != 'undefined') {
5610 Roo.MessageBox.alert("Error loading",arguments[3]);
5614 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5615 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5624 this.el.unmask(this.removeMask);
5629 this.el.unmask(this.removeMask);
5633 onBeforeLoad : function(){
5635 this.el.mask(this.msg, this.msgCls);
5640 destroy : function(){
5642 this.store.un('beforeload', this.onBeforeLoad, this);
5643 this.store.un('load', this.onLoad, this);
5644 this.store.un('loadexception', this.onLoadException, this);
5646 var um = this.el.getUpdateManager();
5647 um.un('beforeupdate', this.onBeforeLoad, this);
5648 um.un('update', this.onLoad, this);
5649 um.un('failure', this.onLoad, this);
5660 * @class Roo.bootstrap.Table
5661 * @extends Roo.bootstrap.Component
5662 * Bootstrap Table class
5663 * @cfg {String} cls table class
5664 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5665 * @cfg {String} bgcolor Specifies the background color for a table
5666 * @cfg {Number} border Specifies whether the table cells should have borders or not
5667 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5668 * @cfg {Number} cellspacing Specifies the space between cells
5669 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5670 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5671 * @cfg {String} sortable Specifies that the table should be sortable
5672 * @cfg {String} summary Specifies a summary of the content of a table
5673 * @cfg {Number} width Specifies the width of a table
5674 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5676 * @cfg {boolean} striped Should the rows be alternative striped
5677 * @cfg {boolean} bordered Add borders to the table
5678 * @cfg {boolean} hover Add hover highlighting
5679 * @cfg {boolean} condensed Format condensed
5680 * @cfg {boolean} responsive Format condensed
5681 * @cfg {Boolean} loadMask (true|false) default false
5682 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5683 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5684 * @cfg {Boolean} rowSelection (true|false) default false
5685 * @cfg {Boolean} cellSelection (true|false) default false
5686 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5687 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5691 * Create a new Table
5692 * @param {Object} config The config object
5695 Roo.bootstrap.Table = function(config){
5696 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5698 if (config.container) {
5699 // ctor'ed from a Border/panel.grid
5700 this.container = Roo.get(config.container);
5701 this.container.update("");
5702 this.container.setStyle("overflow", "hidden");
5703 this.container.addClass('x-grid-container');
5708 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5709 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5710 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5711 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5715 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5716 this.sm = this.selModel;
5717 this.sm.xmodule = this.xmodule || false;
5719 if (this.cm && typeof(this.cm.config) == 'undefined') {
5720 this.colModel = new Roo.grid.ColumnModel(this.cm);
5721 this.cm = this.colModel;
5722 this.cm.xmodule = this.xmodule || false;
5725 this.store= Roo.factory(this.store, Roo.data);
5726 this.ds = this.store;
5727 this.ds.xmodule = this.xmodule || false;
5730 if (this.footer && this.store) {
5731 this.footer.dataSource = this.ds;
5732 this.footer = Roo.factory(this.footer);
5739 * Fires when a cell is clicked
5740 * @param {Roo.bootstrap.Table} this
5741 * @param {Roo.Element} el
5742 * @param {Number} rowIndex
5743 * @param {Number} columnIndex
5744 * @param {Roo.EventObject} e
5748 * @event celldblclick
5749 * Fires when a cell is double clicked
5750 * @param {Roo.bootstrap.Table} this
5751 * @param {Roo.Element} el
5752 * @param {Number} rowIndex
5753 * @param {Number} columnIndex
5754 * @param {Roo.EventObject} e
5756 "celldblclick" : true,
5759 * Fires when a row is clicked
5760 * @param {Roo.bootstrap.Table} this
5761 * @param {Roo.Element} el
5762 * @param {Number} rowIndex
5763 * @param {Roo.EventObject} e
5767 * @event rowdblclick
5768 * Fires when a row is double clicked
5769 * @param {Roo.bootstrap.Table} this
5770 * @param {Roo.Element} el
5771 * @param {Number} rowIndex
5772 * @param {Roo.EventObject} e
5774 "rowdblclick" : true,
5777 * Fires when a mouseover occur
5778 * @param {Roo.bootstrap.Table} this
5779 * @param {Roo.Element} el
5780 * @param {Number} rowIndex
5781 * @param {Number} columnIndex
5782 * @param {Roo.EventObject} e
5787 * Fires when a mouseout occur
5788 * @param {Roo.bootstrap.Table} this
5789 * @param {Roo.Element} el
5790 * @param {Number} rowIndex
5791 * @param {Number} columnIndex
5792 * @param {Roo.EventObject} e
5797 * Fires when a row is rendered, so you can change add a style to it.
5798 * @param {Roo.bootstrap.Table} this
5799 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5803 * @event rowsrendered
5804 * Fires when all the rows have been rendered
5805 * @param {Roo.bootstrap.Table} this
5807 'rowsrendered' : true,
5809 * @event contextmenu
5810 * The raw contextmenu event for the entire grid.
5811 * @param {Roo.EventObject} e
5813 "contextmenu" : true,
5815 * @event rowcontextmenu
5816 * Fires when a row is right clicked
5817 * @param {Roo.bootstrap.Table} this
5818 * @param {Number} rowIndex
5819 * @param {Roo.EventObject} e
5821 "rowcontextmenu" : true,
5823 * @event cellcontextmenu
5824 * Fires when a cell is right clicked
5825 * @param {Roo.bootstrap.Table} this
5826 * @param {Number} rowIndex
5827 * @param {Number} cellIndex
5828 * @param {Roo.EventObject} e
5830 "cellcontextmenu" : true,
5832 * @event headercontextmenu
5833 * Fires when a header is right clicked
5834 * @param {Roo.bootstrap.Table} this
5835 * @param {Number} columnIndex
5836 * @param {Roo.EventObject} e
5838 "headercontextmenu" : true
5842 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5868 rowSelection : false,
5869 cellSelection : false,
5872 // Roo.Element - the tbody
5874 // Roo.Element - thead element
5877 container: false, // used by gridpanel...
5879 getAutoCreate : function()
5881 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5888 if (this.scrollBody) {
5889 cfg.cls += ' table-body-fixed';
5892 cfg.cls += ' table-striped';
5896 cfg.cls += ' table-hover';
5898 if (this.bordered) {
5899 cfg.cls += ' table-bordered';
5901 if (this.condensed) {
5902 cfg.cls += ' table-condensed';
5904 if (this.responsive) {
5905 cfg.cls += ' table-responsive';
5909 cfg.cls+= ' ' +this.cls;
5912 // this lot should be simplifed...
5915 cfg.align=this.align;
5918 cfg.bgcolor=this.bgcolor;
5921 cfg.border=this.border;
5923 if (this.cellpadding) {
5924 cfg.cellpadding=this.cellpadding;
5926 if (this.cellspacing) {
5927 cfg.cellspacing=this.cellspacing;
5930 cfg.frame=this.frame;
5933 cfg.rules=this.rules;
5935 if (this.sortable) {
5936 cfg.sortable=this.sortable;
5939 cfg.summary=this.summary;
5942 cfg.width=this.width;
5945 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5948 if(this.store || this.cm){
5949 if(this.headerShow){
5950 cfg.cn.push(this.renderHeader());
5953 cfg.cn.push(this.renderBody());
5955 if(this.footerShow){
5956 cfg.cn.push(this.renderFooter());
5958 // where does this come from?
5959 //cfg.cls+= ' TableGrid';
5962 return { cn : [ cfg ] };
5965 initEvents : function()
5967 if(!this.store || !this.cm){
5971 //Roo.log('initEvents with ds!!!!');
5973 this.mainBody = this.el.select('tbody', true).first();
5974 this.mainHead = this.el.select('thead', true).first();
5980 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5981 e.on('click', _this.sort, _this);
5984 this.el.on("click", this.onClick, this);
5985 this.el.on("dblclick", this.onDblClick, this);
5987 // why is this done????? = it breaks dialogs??
5988 //this.parent().el.setStyle('position', 'relative');
5992 this.footer.parentId = this.id;
5993 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5996 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5998 this.store.on('load', this.onLoad, this);
5999 this.store.on('beforeload', this.onBeforeLoad, this);
6000 this.store.on('update', this.onUpdate, this);
6001 this.store.on('add', this.onAdd, this);
6003 this.el.on("contextmenu", this.onContextMenu, this);
6005 this.mainBody.on('scroll', this.onBodyScroll, this);
6010 onContextMenu : function(e, t)
6012 this.processEvent("contextmenu", e);
6015 processEvent : function(name, e)
6017 if (name != 'touchstart' ) {
6018 this.fireEvent(name, e);
6021 var t = e.getTarget();
6023 var cell = Roo.get(t);
6029 if(cell.findParent('tfoot', false, true)){
6033 if(cell.findParent('thead', false, true)){
6035 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6036 cell = Roo.get(t).findParent('th', false, true);
6038 Roo.log("failed to find th in thead?");
6039 Roo.log(e.getTarget());
6044 var cellIndex = cell.dom.cellIndex;
6046 var ename = name == 'touchstart' ? 'click' : name;
6047 this.fireEvent("header" + ename, this, cellIndex, e);
6052 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6053 cell = Roo.get(t).findParent('td', false, true);
6055 Roo.log("failed to find th in tbody?");
6056 Roo.log(e.getTarget());
6061 var row = cell.findParent('tr', false, true);
6062 var cellIndex = cell.dom.cellIndex;
6063 var rowIndex = row.dom.rowIndex - 1;
6067 this.fireEvent("row" + name, this, rowIndex, e);
6071 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6077 onMouseover : function(e, el)
6079 var cell = Roo.get(el);
6085 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6086 cell = cell.findParent('td', false, true);
6089 var row = cell.findParent('tr', false, true);
6090 var cellIndex = cell.dom.cellIndex;
6091 var rowIndex = row.dom.rowIndex - 1; // start from 0
6093 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6097 onMouseout : function(e, el)
6099 var cell = Roo.get(el);
6105 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6106 cell = cell.findParent('td', false, true);
6109 var row = cell.findParent('tr', false, true);
6110 var cellIndex = cell.dom.cellIndex;
6111 var rowIndex = row.dom.rowIndex - 1; // start from 0
6113 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6117 onClick : function(e, el)
6119 var cell = Roo.get(el);
6121 if(!cell || (!this.cellSelection && !this.rowSelection)){
6125 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6126 cell = cell.findParent('td', false, true);
6129 if(!cell || typeof(cell) == 'undefined'){
6133 var row = cell.findParent('tr', false, true);
6135 if(!row || typeof(row) == 'undefined'){
6139 var cellIndex = cell.dom.cellIndex;
6140 var rowIndex = this.getRowIndex(row);
6142 // why??? - should these not be based on SelectionModel?
6143 if(this.cellSelection){
6144 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6147 if(this.rowSelection){
6148 this.fireEvent('rowclick', this, row, rowIndex, e);
6154 onDblClick : function(e,el)
6156 var cell = Roo.get(el);
6158 if(!cell || (!this.CellSelection && !this.RowSelection)){
6162 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6163 cell = cell.findParent('td', false, true);
6166 if(!cell || typeof(cell) == 'undefined'){
6170 var row = cell.findParent('tr', false, true);
6172 if(!row || typeof(row) == 'undefined'){
6176 var cellIndex = cell.dom.cellIndex;
6177 var rowIndex = this.getRowIndex(row);
6179 if(this.CellSelection){
6180 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6183 if(this.RowSelection){
6184 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6188 sort : function(e,el)
6190 var col = Roo.get(el);
6192 if(!col.hasClass('sortable')){
6196 var sort = col.attr('sort');
6199 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6203 this.store.sortInfo = {field : sort, direction : dir};
6206 Roo.log("calling footer first");
6207 this.footer.onClick('first');
6210 this.store.load({ params : { start : 0 } });
6214 renderHeader : function()
6222 this.totalWidth = 0;
6224 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6226 var config = cm.config[i];
6231 html: cm.getColumnHeader(i)
6236 if(typeof(config.sortable) != 'undefined' && config.sortable){
6238 c.html = '<i class="glyphicon"></i>' + c.html;
6241 if(typeof(config.lgHeader) != 'undefined'){
6242 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6245 if(typeof(config.mdHeader) != 'undefined'){
6246 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6249 if(typeof(config.smHeader) != 'undefined'){
6250 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6253 if(typeof(config.xsHeader) != 'undefined'){
6254 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6261 if(typeof(config.tooltip) != 'undefined'){
6262 c.tooltip = config.tooltip;
6265 if(typeof(config.colspan) != 'undefined'){
6266 c.colspan = config.colspan;
6269 if(typeof(config.hidden) != 'undefined' && config.hidden){
6270 c.style += ' display:none;';
6273 if(typeof(config.dataIndex) != 'undefined'){
6274 c.sort = config.dataIndex;
6279 if(typeof(config.align) != 'undefined' && config.align.length){
6280 c.style += ' text-align:' + config.align + ';';
6283 if(typeof(config.width) != 'undefined'){
6284 c.style += ' width:' + config.width + 'px;';
6285 this.totalWidth += config.width;
6287 this.totalWidth += 100; // assume minimum of 100 per column?
6290 if(typeof(config.cls) != 'undefined'){
6291 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6294 ['xs','sm','md','lg'].map(function(size){
6296 if(typeof(config[size]) == 'undefined'){
6300 if (!config[size]) { // 0 = hidden
6301 c.cls += ' hidden-' + size;
6305 c.cls += ' col-' + size + '-' + config[size];
6315 renderBody : function()
6325 colspan : this.cm.getColumnCount()
6335 renderFooter : function()
6345 colspan : this.cm.getColumnCount()
6359 // Roo.log('ds onload');
6364 var ds = this.store;
6366 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6367 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6368 if (_this.store.sortInfo) {
6370 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6371 e.select('i', true).addClass(['glyphicon-arrow-up']);
6374 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6375 e.select('i', true).addClass(['glyphicon-arrow-down']);
6380 var tbody = this.mainBody;
6382 if(ds.getCount() > 0){
6383 ds.data.each(function(d,rowIndex){
6384 var row = this.renderRow(cm, ds, rowIndex);
6386 tbody.createChild(row);
6390 if(row.cellObjects.length){
6391 Roo.each(row.cellObjects, function(r){
6392 _this.renderCellObject(r);
6399 Roo.each(this.el.select('tbody td', true).elements, function(e){
6400 e.on('mouseover', _this.onMouseover, _this);
6403 Roo.each(this.el.select('tbody td', true).elements, function(e){
6404 e.on('mouseout', _this.onMouseout, _this);
6406 this.fireEvent('rowsrendered', this);
6407 //if(this.loadMask){
6408 // this.maskEl.hide();
6415 onUpdate : function(ds,record)
6417 this.refreshRow(record);
6420 onRemove : function(ds, record, index, isUpdate){
6421 if(isUpdate !== true){
6422 this.fireEvent("beforerowremoved", this, index, record);
6424 var bt = this.mainBody.dom;
6426 var rows = this.el.select('tbody > tr', true).elements;
6428 if(typeof(rows[index]) != 'undefined'){
6429 bt.removeChild(rows[index].dom);
6432 // if(bt.rows[index]){
6433 // bt.removeChild(bt.rows[index]);
6436 if(isUpdate !== true){
6437 //this.stripeRows(index);
6438 //this.syncRowHeights(index, index);
6440 this.fireEvent("rowremoved", this, index, record);
6444 onAdd : function(ds, records, rowIndex)
6446 //Roo.log('on Add called');
6447 // - note this does not handle multiple adding very well..
6448 var bt = this.mainBody.dom;
6449 for (var i =0 ; i < records.length;i++) {
6450 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6451 //Roo.log(records[i]);
6452 //Roo.log(this.store.getAt(rowIndex+i));
6453 this.insertRow(this.store, rowIndex + i, false);
6460 refreshRow : function(record){
6461 var ds = this.store, index;
6462 if(typeof record == 'number'){
6464 record = ds.getAt(index);
6466 index = ds.indexOf(record);
6468 this.insertRow(ds, index, true);
6469 this.onRemove(ds, record, index+1, true);
6470 //this.syncRowHeights(index, index);
6472 this.fireEvent("rowupdated", this, index, record);
6475 insertRow : function(dm, rowIndex, isUpdate){
6478 this.fireEvent("beforerowsinserted", this, rowIndex);
6480 //var s = this.getScrollState();
6481 var row = this.renderRow(this.cm, this.store, rowIndex);
6482 // insert before rowIndex..
6483 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6487 if(row.cellObjects.length){
6488 Roo.each(row.cellObjects, function(r){
6489 _this.renderCellObject(r);
6494 this.fireEvent("rowsinserted", this, rowIndex);
6495 //this.syncRowHeights(firstRow, lastRow);
6496 //this.stripeRows(firstRow);
6503 getRowDom : function(rowIndex)
6505 var rows = this.el.select('tbody > tr', true).elements;
6507 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6510 // returns the object tree for a tr..
6513 renderRow : function(cm, ds, rowIndex)
6516 var d = ds.getAt(rowIndex);
6523 var cellObjects = [];
6525 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6526 var config = cm.config[i];
6528 var renderer = cm.getRenderer(i);
6532 if(typeof(renderer) !== 'undefined'){
6533 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6535 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6536 // and are rendered into the cells after the row is rendered - using the id for the element.
6538 if(typeof(value) === 'object'){
6548 rowIndex : rowIndex,
6553 this.fireEvent('rowclass', this, rowcfg);
6557 cls : rowcfg.rowClass,
6559 html: (typeof(value) === 'object') ? '' : value
6566 if(typeof(config.colspan) != 'undefined'){
6567 td.colspan = config.colspan;
6570 if(typeof(config.hidden) != 'undefined' && config.hidden){
6571 td.style += ' display:none;';
6574 if(typeof(config.align) != 'undefined' && config.align.length){
6575 td.style += ' text-align:' + config.align + ';';
6578 if(typeof(config.width) != 'undefined'){
6579 td.style += ' width:' + config.width + 'px;';
6582 if(typeof(config.cursor) != 'undefined'){
6583 td.style += ' cursor:' + config.cursor + ';';
6586 if(typeof(config.cls) != 'undefined'){
6587 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6590 ['xs','sm','md','lg'].map(function(size){
6592 if(typeof(config[size]) == 'undefined'){
6596 if (!config[size]) { // 0 = hidden
6597 td.cls += ' hidden-' + size;
6601 td.cls += ' col-' + size + '-' + config[size];
6609 row.cellObjects = cellObjects;
6617 onBeforeLoad : function()
6619 //Roo.log('ds onBeforeLoad');
6623 //if(this.loadMask){
6624 // this.maskEl.show();
6632 this.el.select('tbody', true).first().dom.innerHTML = '';
6635 * Show or hide a row.
6636 * @param {Number} rowIndex to show or hide
6637 * @param {Boolean} state hide
6639 setRowVisibility : function(rowIndex, state)
6641 var bt = this.mainBody.dom;
6643 var rows = this.el.select('tbody > tr', true).elements;
6645 if(typeof(rows[rowIndex]) == 'undefined'){
6648 rows[rowIndex].dom.style.display = state ? '' : 'none';
6652 getSelectionModel : function(){
6654 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6656 return this.selModel;
6659 * Render the Roo.bootstrap object from renderder
6661 renderCellObject : function(r)
6665 var t = r.cfg.render(r.container);
6668 Roo.each(r.cfg.cn, function(c){
6670 container: t.getChildContainer(),
6673 _this.renderCellObject(child);
6678 getRowIndex : function(row)
6682 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6693 * Returns the grid's underlying element = used by panel.Grid
6694 * @return {Element} The element
6696 getGridEl : function(){
6697 return this.container;
6700 * Forces a resize - used by panel.Grid
6701 * @return {Element} The element
6703 autoSize : function(){
6704 //var ctr = Roo.get(this.container.dom.parentElement);
6705 var ctr = Roo.get(this.container.dom);
6707 var thd = this.getGridEl().select('thead',true).first();
6708 var tbd = this.getGridEl().select('tbody', true).first();
6711 var cw = ctr.getWidth();
6715 tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6716 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6719 cw = Math.max(cw, this.totalWidth);
6720 this.getGridEl().select('tr',true).setWidth(cw);
6722 return; // we doe not have a view in this design..
6725 onBodyScroll: function()
6728 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6729 this.mainHead.setStyle({
6730 'position' : 'relative',
6731 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6748 * @class Roo.bootstrap.TableCell
6749 * @extends Roo.bootstrap.Component
6750 * Bootstrap TableCell class
6751 * @cfg {String} html cell contain text
6752 * @cfg {String} cls cell class
6753 * @cfg {String} tag cell tag (td|th) default td
6754 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6755 * @cfg {String} align Aligns the content in a cell
6756 * @cfg {String} axis Categorizes cells
6757 * @cfg {String} bgcolor Specifies the background color of a cell
6758 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6759 * @cfg {Number} colspan Specifies the number of columns a cell should span
6760 * @cfg {String} headers Specifies one or more header cells a cell is related to
6761 * @cfg {Number} height Sets the height of a cell
6762 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6763 * @cfg {Number} rowspan Sets the number of rows a cell should span
6764 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6765 * @cfg {String} valign Vertical aligns the content in a cell
6766 * @cfg {Number} width Specifies the width of a cell
6769 * Create a new TableCell
6770 * @param {Object} config The config object
6773 Roo.bootstrap.TableCell = function(config){
6774 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6777 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6797 getAutoCreate : function(){
6798 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6818 cfg.align=this.align
6824 cfg.bgcolor=this.bgcolor
6827 cfg.charoff=this.charoff
6830 cfg.colspan=this.colspan
6833 cfg.headers=this.headers
6836 cfg.height=this.height
6839 cfg.nowrap=this.nowrap
6842 cfg.rowspan=this.rowspan
6845 cfg.scope=this.scope
6848 cfg.valign=this.valign
6851 cfg.width=this.width
6870 * @class Roo.bootstrap.TableRow
6871 * @extends Roo.bootstrap.Component
6872 * Bootstrap TableRow class
6873 * @cfg {String} cls row class
6874 * @cfg {String} align Aligns the content in a table row
6875 * @cfg {String} bgcolor Specifies a background color for a table row
6876 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6877 * @cfg {String} valign Vertical aligns the content in a table row
6880 * Create a new TableRow
6881 * @param {Object} config The config object
6884 Roo.bootstrap.TableRow = function(config){
6885 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6888 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6896 getAutoCreate : function(){
6897 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6907 cfg.align = this.align;
6910 cfg.bgcolor = this.bgcolor;
6913 cfg.charoff = this.charoff;
6916 cfg.valign = this.valign;
6934 * @class Roo.bootstrap.TableBody
6935 * @extends Roo.bootstrap.Component
6936 * Bootstrap TableBody class
6937 * @cfg {String} cls element class
6938 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6939 * @cfg {String} align Aligns the content inside the element
6940 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6941 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6944 * Create a new TableBody
6945 * @param {Object} config The config object
6948 Roo.bootstrap.TableBody = function(config){
6949 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6952 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6960 getAutoCreate : function(){
6961 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6975 cfg.align = this.align;
6978 cfg.charoff = this.charoff;
6981 cfg.valign = this.valign;
6988 // initEvents : function()
6995 // this.store = Roo.factory(this.store, Roo.data);
6996 // this.store.on('load', this.onLoad, this);
6998 // this.store.load();
7002 // onLoad: function ()
7004 // this.fireEvent('load', this);
7014 * Ext JS Library 1.1.1
7015 * Copyright(c) 2006-2007, Ext JS, LLC.
7017 * Originally Released Under LGPL - original licence link has changed is not relivant.
7020 * <script type="text/javascript">
7023 // as we use this in bootstrap.
7024 Roo.namespace('Roo.form');
7026 * @class Roo.form.Action
7027 * Internal Class used to handle form actions
7029 * @param {Roo.form.BasicForm} el The form element or its id
7030 * @param {Object} config Configuration options
7035 // define the action interface
7036 Roo.form.Action = function(form, options){
7038 this.options = options || {};
7041 * Client Validation Failed
7044 Roo.form.Action.CLIENT_INVALID = 'client';
7046 * Server Validation Failed
7049 Roo.form.Action.SERVER_INVALID = 'server';
7051 * Connect to Server Failed
7054 Roo.form.Action.CONNECT_FAILURE = 'connect';
7056 * Reading Data from Server Failed
7059 Roo.form.Action.LOAD_FAILURE = 'load';
7061 Roo.form.Action.prototype = {
7063 failureType : undefined,
7064 response : undefined,
7068 run : function(options){
7073 success : function(response){
7078 handleResponse : function(response){
7082 // default connection failure
7083 failure : function(response){
7085 this.response = response;
7086 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7087 this.form.afterAction(this, false);
7090 processResponse : function(response){
7091 this.response = response;
7092 if(!response.responseText){
7095 this.result = this.handleResponse(response);
7099 // utility functions used internally
7100 getUrl : function(appendParams){
7101 var url = this.options.url || this.form.url || this.form.el.dom.action;
7103 var p = this.getParams();
7105 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7111 getMethod : function(){
7112 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7115 getParams : function(){
7116 var bp = this.form.baseParams;
7117 var p = this.options.params;
7119 if(typeof p == "object"){
7120 p = Roo.urlEncode(Roo.applyIf(p, bp));
7121 }else if(typeof p == 'string' && bp){
7122 p += '&' + Roo.urlEncode(bp);
7125 p = Roo.urlEncode(bp);
7130 createCallback : function(){
7132 success: this.success,
7133 failure: this.failure,
7135 timeout: (this.form.timeout*1000),
7136 upload: this.form.fileUpload ? this.success : undefined
7141 Roo.form.Action.Submit = function(form, options){
7142 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7145 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7148 haveProgress : false,
7149 uploadComplete : false,
7151 // uploadProgress indicator.
7152 uploadProgress : function()
7154 if (!this.form.progressUrl) {
7158 if (!this.haveProgress) {
7159 Roo.MessageBox.progress("Uploading", "Uploading");
7161 if (this.uploadComplete) {
7162 Roo.MessageBox.hide();
7166 this.haveProgress = true;
7168 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7170 var c = new Roo.data.Connection();
7172 url : this.form.progressUrl,
7177 success : function(req){
7178 //console.log(data);
7182 rdata = Roo.decode(req.responseText)
7184 Roo.log("Invalid data from server..");
7188 if (!rdata || !rdata.success) {
7190 Roo.MessageBox.alert(Roo.encode(rdata));
7193 var data = rdata.data;
7195 if (this.uploadComplete) {
7196 Roo.MessageBox.hide();
7201 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7202 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7205 this.uploadProgress.defer(2000,this);
7208 failure: function(data) {
7209 Roo.log('progress url failed ');
7220 // run get Values on the form, so it syncs any secondary forms.
7221 this.form.getValues();
7223 var o = this.options;
7224 var method = this.getMethod();
7225 var isPost = method == 'POST';
7226 if(o.clientValidation === false || this.form.isValid()){
7228 if (this.form.progressUrl) {
7229 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7230 (new Date() * 1) + '' + Math.random());
7235 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7236 form:this.form.el.dom,
7237 url:this.getUrl(!isPost),
7239 params:isPost ? this.getParams() : null,
7240 isUpload: this.form.fileUpload
7243 this.uploadProgress();
7245 }else if (o.clientValidation !== false){ // client validation failed
7246 this.failureType = Roo.form.Action.CLIENT_INVALID;
7247 this.form.afterAction(this, false);
7251 success : function(response)
7253 this.uploadComplete= true;
7254 if (this.haveProgress) {
7255 Roo.MessageBox.hide();
7259 var result = this.processResponse(response);
7260 if(result === true || result.success){
7261 this.form.afterAction(this, true);
7265 this.form.markInvalid(result.errors);
7266 this.failureType = Roo.form.Action.SERVER_INVALID;
7268 this.form.afterAction(this, false);
7270 failure : function(response)
7272 this.uploadComplete= true;
7273 if (this.haveProgress) {
7274 Roo.MessageBox.hide();
7277 this.response = response;
7278 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7279 this.form.afterAction(this, false);
7282 handleResponse : function(response){
7283 if(this.form.errorReader){
7284 var rs = this.form.errorReader.read(response);
7287 for(var i = 0, len = rs.records.length; i < len; i++) {
7288 var r = rs.records[i];
7292 if(errors.length < 1){
7296 success : rs.success,
7302 ret = Roo.decode(response.responseText);
7306 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7316 Roo.form.Action.Load = function(form, options){
7317 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7318 this.reader = this.form.reader;
7321 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7326 Roo.Ajax.request(Roo.apply(
7327 this.createCallback(), {
7328 method:this.getMethod(),
7329 url:this.getUrl(false),
7330 params:this.getParams()
7334 success : function(response){
7336 var result = this.processResponse(response);
7337 if(result === true || !result.success || !result.data){
7338 this.failureType = Roo.form.Action.LOAD_FAILURE;
7339 this.form.afterAction(this, false);
7342 this.form.clearInvalid();
7343 this.form.setValues(result.data);
7344 this.form.afterAction(this, true);
7347 handleResponse : function(response){
7348 if(this.form.reader){
7349 var rs = this.form.reader.read(response);
7350 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7352 success : rs.success,
7356 return Roo.decode(response.responseText);
7360 Roo.form.Action.ACTION_TYPES = {
7361 'load' : Roo.form.Action.Load,
7362 'submit' : Roo.form.Action.Submit
7371 * @class Roo.bootstrap.Form
7372 * @extends Roo.bootstrap.Component
7373 * Bootstrap Form class
7374 * @cfg {String} method GET | POST (default POST)
7375 * @cfg {String} labelAlign top | left (default top)
7376 * @cfg {String} align left | right - for navbars
7377 * @cfg {Boolean} loadMask load mask when submit (default true)
7382 * @param {Object} config The config object
7386 Roo.bootstrap.Form = function(config){
7387 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7390 * @event clientvalidation
7391 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7392 * @param {Form} this
7393 * @param {Boolean} valid true if the form has passed client-side validation
7395 clientvalidation: true,
7397 * @event beforeaction
7398 * Fires before any action is performed. Return false to cancel the action.
7399 * @param {Form} this
7400 * @param {Action} action The action to be performed
7404 * @event actionfailed
7405 * Fires when an action fails.
7406 * @param {Form} this
7407 * @param {Action} action The action that failed
7409 actionfailed : true,
7411 * @event actioncomplete
7412 * Fires when an action is completed.
7413 * @param {Form} this
7414 * @param {Action} action The action that completed
7416 actioncomplete : true
7421 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7424 * @cfg {String} method
7425 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7430 * The URL to use for form actions if one isn't supplied in the action options.
7433 * @cfg {Boolean} fileUpload
7434 * Set to true if this form is a file upload.
7438 * @cfg {Object} baseParams
7439 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7443 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7447 * @cfg {Sting} align (left|right) for navbar forms
7452 activeAction : null,
7455 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7456 * element by passing it or its id or mask the form itself by passing in true.
7459 waitMsgTarget : false,
7463 getAutoCreate : function(){
7467 method : this.method || 'POST',
7468 id : this.id || Roo.id(),
7471 if (this.parent().xtype.match(/^Nav/)) {
7472 cfg.cls = 'navbar-form navbar-' + this.align;
7476 if (this.labelAlign == 'left' ) {
7477 cfg.cls += ' form-horizontal';
7483 initEvents : function()
7485 this.el.on('submit', this.onSubmit, this);
7486 // this was added as random key presses on the form where triggering form submit.
7487 this.el.on('keypress', function(e) {
7488 if (e.getCharCode() != 13) {
7491 // we might need to allow it for textareas.. and some other items.
7492 // check e.getTarget().
7494 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7498 Roo.log("keypress blocked");
7506 onSubmit : function(e){
7511 * Returns true if client-side validation on the form is successful.
7514 isValid : function(){
7515 var items = this.getItems();
7517 items.each(function(f){
7526 * Returns true if any fields in this form have changed since their original load.
7529 isDirty : function(){
7531 var items = this.getItems();
7532 items.each(function(f){
7542 * Performs a predefined action (submit or load) or custom actions you define on this form.
7543 * @param {String} actionName The name of the action type
7544 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7545 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7546 * accept other config options):
7548 Property Type Description
7549 ---------------- --------------- ----------------------------------------------------------------------------------
7550 url String The url for the action (defaults to the form's url)
7551 method String The form method to use (defaults to the form's method, or POST if not defined)
7552 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7553 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7554 validate the form on the client (defaults to false)
7556 * @return {BasicForm} this
7558 doAction : function(action, options){
7559 if(typeof action == 'string'){
7560 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7562 if(this.fireEvent('beforeaction', this, action) !== false){
7563 this.beforeAction(action);
7564 action.run.defer(100, action);
7570 beforeAction : function(action){
7571 var o = action.options;
7574 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7576 // not really supported yet.. ??
7578 //if(this.waitMsgTarget === true){
7579 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7580 //}else if(this.waitMsgTarget){
7581 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7582 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7584 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7590 afterAction : function(action, success){
7591 this.activeAction = null;
7592 var o = action.options;
7594 //if(this.waitMsgTarget === true){
7596 //}else if(this.waitMsgTarget){
7597 // this.waitMsgTarget.unmask();
7599 // Roo.MessageBox.updateProgress(1);
7600 // Roo.MessageBox.hide();
7607 Roo.callback(o.success, o.scope, [this, action]);
7608 this.fireEvent('actioncomplete', this, action);
7612 // failure condition..
7613 // we have a scenario where updates need confirming.
7614 // eg. if a locking scenario exists..
7615 // we look for { errors : { needs_confirm : true }} in the response.
7617 (typeof(action.result) != 'undefined') &&
7618 (typeof(action.result.errors) != 'undefined') &&
7619 (typeof(action.result.errors.needs_confirm) != 'undefined')
7622 Roo.log("not supported yet");
7625 Roo.MessageBox.confirm(
7626 "Change requires confirmation",
7627 action.result.errorMsg,
7632 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7642 Roo.callback(o.failure, o.scope, [this, action]);
7643 // show an error message if no failed handler is set..
7644 if (!this.hasListener('actionfailed')) {
7645 Roo.log("need to add dialog support");
7647 Roo.MessageBox.alert("Error",
7648 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7649 action.result.errorMsg :
7650 "Saving Failed, please check your entries or try again"
7655 this.fireEvent('actionfailed', this, action);
7660 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7661 * @param {String} id The value to search for
7664 findField : function(id){
7665 var items = this.getItems();
7666 var field = items.get(id);
7668 items.each(function(f){
7669 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7676 return field || null;
7679 * Mark fields in this form invalid in bulk.
7680 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7681 * @return {BasicForm} this
7683 markInvalid : function(errors){
7684 if(errors instanceof Array){
7685 for(var i = 0, len = errors.length; i < len; i++){
7686 var fieldError = errors[i];
7687 var f = this.findField(fieldError.id);
7689 f.markInvalid(fieldError.msg);
7695 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7696 field.markInvalid(errors[id]);
7700 //Roo.each(this.childForms || [], function (f) {
7701 // f.markInvalid(errors);
7708 * Set values for fields in this form in bulk.
7709 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7710 * @return {BasicForm} this
7712 setValues : function(values){
7713 if(values instanceof Array){ // array of objects
7714 for(var i = 0, len = values.length; i < len; i++){
7716 var f = this.findField(v.id);
7718 f.setValue(v.value);
7719 if(this.trackResetOnLoad){
7720 f.originalValue = f.getValue();
7724 }else{ // object hash
7727 if(typeof values[id] != 'function' && (field = this.findField(id))){
7729 if (field.setFromData &&
7731 field.displayField &&
7732 // combos' with local stores can
7733 // be queried via setValue()
7734 // to set their value..
7735 (field.store && !field.store.isLocal)
7739 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7740 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7741 field.setFromData(sd);
7744 field.setValue(values[id]);
7748 if(this.trackResetOnLoad){
7749 field.originalValue = field.getValue();
7755 //Roo.each(this.childForms || [], function (f) {
7756 // f.setValues(values);
7763 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7764 * they are returned as an array.
7765 * @param {Boolean} asString
7768 getValues : function(asString){
7769 //if (this.childForms) {
7770 // copy values from the child forms
7771 // Roo.each(this.childForms, function (f) {
7772 // this.setValues(f.getValues());
7778 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7779 if(asString === true){
7782 return Roo.urlDecode(fs);
7786 * Returns the fields in this form as an object with key/value pairs.
7787 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7790 getFieldValues : function(with_hidden)
7792 var items = this.getItems();
7794 items.each(function(f){
7798 var v = f.getValue();
7799 if (f.inputType =='radio') {
7800 if (typeof(ret[f.getName()]) == 'undefined') {
7801 ret[f.getName()] = ''; // empty..
7804 if (!f.el.dom.checked) {
7812 // not sure if this supported any more..
7813 if ((typeof(v) == 'object') && f.getRawValue) {
7814 v = f.getRawValue() ; // dates..
7816 // combo boxes where name != hiddenName...
7817 if (f.name != f.getName()) {
7818 ret[f.name] = f.getRawValue();
7820 ret[f.getName()] = v;
7827 * Clears all invalid messages in this form.
7828 * @return {BasicForm} this
7830 clearInvalid : function(){
7831 var items = this.getItems();
7833 items.each(function(f){
7844 * @return {BasicForm} this
7847 var items = this.getItems();
7848 items.each(function(f){
7852 Roo.each(this.childForms || [], function (f) {
7859 getItems : function()
7861 var r=new Roo.util.MixedCollection(false, function(o){
7862 return o.id || (o.id = Roo.id());
7864 var iter = function(el) {
7871 Roo.each(el.items,function(e) {
7891 * Ext JS Library 1.1.1
7892 * Copyright(c) 2006-2007, Ext JS, LLC.
7894 * Originally Released Under LGPL - original licence link has changed is not relivant.
7897 * <script type="text/javascript">
7900 * @class Roo.form.VTypes
7901 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7904 Roo.form.VTypes = function(){
7905 // closure these in so they are only created once.
7906 var alpha = /^[a-zA-Z_]+$/;
7907 var alphanum = /^[a-zA-Z0-9_]+$/;
7908 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7909 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7911 // All these messages and functions are configurable
7914 * The function used to validate email addresses
7915 * @param {String} value The email address
7917 'email' : function(v){
7918 return email.test(v);
7921 * The error text to display when the email validation function returns false
7924 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7926 * The keystroke filter mask to be applied on email input
7929 'emailMask' : /[a-z0-9_\.\-@]/i,
7932 * The function used to validate URLs
7933 * @param {String} value The URL
7935 'url' : function(v){
7939 * The error text to display when the url validation function returns false
7942 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7945 * The function used to validate alpha values
7946 * @param {String} value The value
7948 'alpha' : function(v){
7949 return alpha.test(v);
7952 * The error text to display when the alpha validation function returns false
7955 'alphaText' : 'This field should only contain letters and _',
7957 * The keystroke filter mask to be applied on alpha input
7960 'alphaMask' : /[a-z_]/i,
7963 * The function used to validate alphanumeric values
7964 * @param {String} value The value
7966 'alphanum' : function(v){
7967 return alphanum.test(v);
7970 * The error text to display when the alphanumeric validation function returns false
7973 'alphanumText' : 'This field should only contain letters, numbers and _',
7975 * The keystroke filter mask to be applied on alphanumeric input
7978 'alphanumMask' : /[a-z0-9_]/i
7988 * @class Roo.bootstrap.Input
7989 * @extends Roo.bootstrap.Component
7990 * Bootstrap Input class
7991 * @cfg {Boolean} disabled is it disabled
7992 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7993 * @cfg {String} name name of the input
7994 * @cfg {string} fieldLabel - the label associated
7995 * @cfg {string} placeholder - placeholder to put in text.
7996 * @cfg {string} before - input group add on before
7997 * @cfg {string} after - input group add on after
7998 * @cfg {string} size - (lg|sm) or leave empty..
7999 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8000 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8001 * @cfg {Number} md colspan out of 12 for computer-sized screens
8002 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8003 * @cfg {string} value default value of the input
8004 * @cfg {Number} labelWidth set the width of label (0-12)
8005 * @cfg {String} labelAlign (top|left)
8006 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8007 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8009 * @cfg {String} align (left|center|right) Default left
8010 * @cfg {Boolean} forceFeedback (true|false) Default false
8016 * Create a new Input
8017 * @param {Object} config The config object
8020 Roo.bootstrap.Input = function(config){
8021 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8026 * Fires when this field receives input focus.
8027 * @param {Roo.form.Field} this
8032 * Fires when this field loses input focus.
8033 * @param {Roo.form.Field} this
8038 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8039 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8040 * @param {Roo.form.Field} this
8041 * @param {Roo.EventObject} e The event object
8046 * Fires just before the field blurs if the field value has changed.
8047 * @param {Roo.form.Field} this
8048 * @param {Mixed} newValue The new value
8049 * @param {Mixed} oldValue The original value
8054 * Fires after the field has been marked as invalid.
8055 * @param {Roo.form.Field} this
8056 * @param {String} msg The validation message
8061 * Fires after the field has been validated with no errors.
8062 * @param {Roo.form.Field} this
8067 * Fires after the key up
8068 * @param {Roo.form.Field} this
8069 * @param {Roo.EventObject} e The event Object
8075 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8077 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8078 automatic validation (defaults to "keyup").
8080 validationEvent : "keyup",
8082 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8084 validateOnBlur : true,
8086 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8088 validationDelay : 250,
8090 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8092 focusClass : "x-form-focus", // not needed???
8096 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8098 invalidClass : "has-warning",
8101 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8103 validClass : "has-success",
8106 * @cfg {Boolean} hasFeedback (true|false) default true
8111 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8113 invalidFeedbackClass : "glyphicon-warning-sign",
8116 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8118 validFeedbackClass : "glyphicon-ok",
8121 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8123 selectOnFocus : false,
8126 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8130 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8135 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8137 disableKeyFilter : false,
8140 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8144 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8148 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8150 blankText : "This field is required",
8153 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8157 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8159 maxLength : Number.MAX_VALUE,
8161 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8163 minLengthText : "The minimum length for this field is {0}",
8165 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8167 maxLengthText : "The maximum length for this field is {0}",
8171 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8172 * If available, this function will be called only after the basic validators all return true, and will be passed the
8173 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8177 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8178 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8179 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8183 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8187 autocomplete: false,
8206 formatedValue : false,
8207 forceFeedback : false,
8209 parentLabelAlign : function()
8212 while (parent.parent()) {
8213 parent = parent.parent();
8214 if (typeof(parent.labelAlign) !='undefined') {
8215 return parent.labelAlign;
8222 getAutoCreate : function(){
8224 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8232 if(this.inputType != 'hidden'){
8233 cfg.cls = 'form-group' //input-group
8239 type : this.inputType,
8241 cls : 'form-control',
8242 placeholder : this.placeholder || '',
8243 autocomplete : this.autocomplete || 'new-password'
8248 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8251 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8252 input.maxLength = this.maxLength;
8255 if (this.disabled) {
8256 input.disabled=true;
8259 if (this.readOnly) {
8260 input.readonly=true;
8264 input.name = this.name;
8267 input.cls += ' input-' + this.size;
8270 ['xs','sm','md','lg'].map(function(size){
8271 if (settings[size]) {
8272 cfg.cls += ' col-' + size + '-' + settings[size];
8276 var inputblock = input;
8280 cls: 'glyphicon form-control-feedback'
8283 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8286 cls : 'has-feedback',
8294 if (this.before || this.after) {
8297 cls : 'input-group',
8301 if (this.before && typeof(this.before) == 'string') {
8303 inputblock.cn.push({
8305 cls : 'roo-input-before input-group-addon',
8309 if (this.before && typeof(this.before) == 'object') {
8310 this.before = Roo.factory(this.before);
8312 inputblock.cn.push({
8314 cls : 'roo-input-before input-group-' +
8315 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8319 inputblock.cn.push(input);
8321 if (this.after && typeof(this.after) == 'string') {
8322 inputblock.cn.push({
8324 cls : 'roo-input-after input-group-addon',
8328 if (this.after && typeof(this.after) == 'object') {
8329 this.after = Roo.factory(this.after);
8331 inputblock.cn.push({
8333 cls : 'roo-input-after input-group-' +
8334 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8338 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8339 inputblock.cls += ' has-feedback';
8340 inputblock.cn.push(feedback);
8344 if (align ==='left' && this.fieldLabel.length) {
8351 cls : 'control-label col-sm-' + this.labelWidth,
8352 html : this.fieldLabel
8356 cls : "col-sm-" + (12 - this.labelWidth),
8363 } else if ( this.fieldLabel.length) {
8369 //cls : 'input-group-addon',
8370 html : this.fieldLabel
8389 if (this.parentType === 'Navbar' && this.parent().bar) {
8390 cfg.cls += ' navbar-form';
8392 if (this.parentType === 'NavGroup') {
8393 cfg.cls += ' navbar-form';
8400 * return the real input element.
8402 inputEl: function ()
8404 return this.el.select('input.form-control',true).first();
8407 tooltipEl : function()
8409 return this.inputEl();
8412 setDisabled : function(v)
8414 var i = this.inputEl().dom;
8416 i.removeAttribute('disabled');
8420 i.setAttribute('disabled','true');
8422 initEvents : function()
8425 this.inputEl().on("keydown" , this.fireKey, this);
8426 this.inputEl().on("focus", this.onFocus, this);
8427 this.inputEl().on("blur", this.onBlur, this);
8429 this.inputEl().relayEvent('keyup', this);
8431 // reference to original value for reset
8432 this.originalValue = this.getValue();
8433 //Roo.form.TextField.superclass.initEvents.call(this);
8434 if(this.validationEvent == 'keyup'){
8435 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8436 this.inputEl().on('keyup', this.filterValidation, this);
8438 else if(this.validationEvent !== false){
8439 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8442 if(this.selectOnFocus){
8443 this.on("focus", this.preFocus, this);
8446 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8447 this.inputEl().on("keypress", this.filterKeys, this);
8450 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8451 this.el.on("click", this.autoSize, this);
8454 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8455 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8458 if (typeof(this.before) == 'object') {
8459 this.before.render(this.el.select('.roo-input-before',true).first());
8461 if (typeof(this.after) == 'object') {
8462 this.after.render(this.el.select('.roo-input-after',true).first());
8467 filterValidation : function(e){
8468 if(!e.isNavKeyPress()){
8469 this.validationTask.delay(this.validationDelay);
8473 * Validates the field value
8474 * @return {Boolean} True if the value is valid, else false
8476 validate : function(){
8477 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8478 if(this.disabled || this.validateValue(this.getRawValue())){
8489 * Validates a value according to the field's validation rules and marks the field as invalid
8490 * if the validation fails
8491 * @param {Mixed} value The value to validate
8492 * @return {Boolean} True if the value is valid, else false
8494 validateValue : function(value){
8495 if(value.length < 1) { // if it's blank
8496 if(this.allowBlank){
8502 if(value.length < this.minLength){
8505 if(value.length > this.maxLength){
8509 var vt = Roo.form.VTypes;
8510 if(!vt[this.vtype](value, this)){
8514 if(typeof this.validator == "function"){
8515 var msg = this.validator(value);
8521 if(this.regex && !this.regex.test(value)){
8531 fireKey : function(e){
8532 //Roo.log('field ' + e.getKey());
8533 if(e.isNavKeyPress()){
8534 this.fireEvent("specialkey", this, e);
8537 focus : function (selectText){
8539 this.inputEl().focus();
8540 if(selectText === true){
8541 this.inputEl().dom.select();
8547 onFocus : function(){
8548 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8549 // this.el.addClass(this.focusClass);
8552 this.hasFocus = true;
8553 this.startValue = this.getValue();
8554 this.fireEvent("focus", this);
8558 beforeBlur : Roo.emptyFn,
8562 onBlur : function(){
8564 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8565 //this.el.removeClass(this.focusClass);
8567 this.hasFocus = false;
8568 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8571 var v = this.getValue();
8572 if(String(v) !== String(this.startValue)){
8573 this.fireEvent('change', this, v, this.startValue);
8575 this.fireEvent("blur", this);
8579 * Resets the current field value to the originally loaded value and clears any validation messages
8582 this.setValue(this.originalValue);
8586 * Returns the name of the field
8587 * @return {Mixed} name The name field
8589 getName: function(){
8593 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8594 * @return {Mixed} value The field value
8596 getValue : function(){
8598 var v = this.inputEl().getValue();
8603 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8604 * @return {Mixed} value The field value
8606 getRawValue : function(){
8607 var v = this.inputEl().getValue();
8613 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8614 * @param {Mixed} value The value to set
8616 setRawValue : function(v){
8617 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8620 selectText : function(start, end){
8621 var v = this.getRawValue();
8623 start = start === undefined ? 0 : start;
8624 end = end === undefined ? v.length : end;
8625 var d = this.inputEl().dom;
8626 if(d.setSelectionRange){
8627 d.setSelectionRange(start, end);
8628 }else if(d.createTextRange){
8629 var range = d.createTextRange();
8630 range.moveStart("character", start);
8631 range.moveEnd("character", v.length-end);
8638 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8639 * @param {Mixed} value The value to set
8641 setValue : function(v){
8644 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8650 processValue : function(value){
8651 if(this.stripCharsRe){
8652 var newValue = value.replace(this.stripCharsRe, '');
8653 if(newValue !== value){
8654 this.setRawValue(newValue);
8661 preFocus : function(){
8663 if(this.selectOnFocus){
8664 this.inputEl().dom.select();
8667 filterKeys : function(e){
8669 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8672 var c = e.getCharCode(), cc = String.fromCharCode(c);
8673 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8676 if(!this.maskRe.test(cc)){
8681 * Clear any invalid styles/messages for this field
8683 clearInvalid : function(){
8685 if(!this.el || this.preventMark){ // not rendered
8689 var label = this.el.select('label', true).first();
8690 var icon = this.el.select('i.fa-star', true).first();
8696 this.el.removeClass(this.invalidClass);
8698 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8700 var feedback = this.el.select('.form-control-feedback', true).first();
8703 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8708 this.fireEvent('valid', this);
8712 * Mark this field as valid
8714 markValid : function()
8716 if(!this.el || this.preventMark){ // not rendered
8720 this.el.removeClass([this.invalidClass, this.validClass]);
8722 var feedback = this.el.select('.form-control-feedback', true).first();
8725 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8728 if(this.disabled || this.allowBlank){
8732 var formGroup = this.el.findParent('.form-group', false, true);
8736 var label = formGroup.select('label', true).first();
8737 var icon = formGroup.select('i.fa-star', true).first();
8744 this.el.addClass(this.validClass);
8746 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8748 var feedback = this.el.select('.form-control-feedback', true).first();
8751 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8752 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8757 this.fireEvent('valid', this);
8761 * Mark this field as invalid
8762 * @param {String} msg The validation message
8764 markInvalid : function(msg)
8766 if(!this.el || this.preventMark){ // not rendered
8770 this.el.removeClass([this.invalidClass, this.validClass]);
8772 var feedback = this.el.select('.form-control-feedback', true).first();
8775 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8778 if(this.disabled || this.allowBlank){
8782 var formGroup = this.el.findParent('.form-group', false, true);
8785 var label = formGroup.select('label', true).first();
8786 var icon = formGroup.select('i.fa-star', true).first();
8788 if(!this.getValue().length && label && !icon){
8789 this.el.findParent('.form-group', false, true).createChild({
8791 cls : 'text-danger fa fa-lg fa-star',
8792 tooltip : 'This field is required',
8793 style : 'margin-right:5px;'
8799 this.el.addClass(this.invalidClass);
8801 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8803 var feedback = this.el.select('.form-control-feedback', true).first();
8806 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8808 if(this.getValue().length || this.forceFeedback){
8809 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8816 this.fireEvent('invalid', this, msg);
8819 SafariOnKeyDown : function(event)
8821 // this is a workaround for a password hang bug on chrome/ webkit.
8823 var isSelectAll = false;
8825 if(this.inputEl().dom.selectionEnd > 0){
8826 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8828 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8829 event.preventDefault();
8834 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8836 event.preventDefault();
8837 // this is very hacky as keydown always get's upper case.
8839 var cc = String.fromCharCode(event.getCharCode());
8840 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8844 adjustWidth : function(tag, w){
8845 tag = tag.toLowerCase();
8846 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8847 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8851 if(tag == 'textarea'){
8854 }else if(Roo.isOpera){
8858 if(tag == 'textarea'){
8877 * @class Roo.bootstrap.TextArea
8878 * @extends Roo.bootstrap.Input
8879 * Bootstrap TextArea class
8880 * @cfg {Number} cols Specifies the visible width of a text area
8881 * @cfg {Number} rows Specifies the visible number of lines in a text area
8882 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8883 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8884 * @cfg {string} html text
8887 * Create a new TextArea
8888 * @param {Object} config The config object
8891 Roo.bootstrap.TextArea = function(config){
8892 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8896 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8906 getAutoCreate : function(){
8908 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8919 value : this.value || '',
8920 html: this.html || '',
8921 cls : 'form-control',
8922 placeholder : this.placeholder || ''
8926 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8927 input.maxLength = this.maxLength;
8931 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8935 input.cols = this.cols;
8938 if (this.readOnly) {
8939 input.readonly = true;
8943 input.name = this.name;
8947 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8951 ['xs','sm','md','lg'].map(function(size){
8952 if (settings[size]) {
8953 cfg.cls += ' col-' + size + '-' + settings[size];
8957 var inputblock = input;
8959 if(this.hasFeedback && !this.allowBlank){
8963 cls: 'glyphicon form-control-feedback'
8967 cls : 'has-feedback',
8976 if (this.before || this.after) {
8979 cls : 'input-group',
8983 inputblock.cn.push({
8985 cls : 'input-group-addon',
8990 inputblock.cn.push(input);
8992 if(this.hasFeedback && !this.allowBlank){
8993 inputblock.cls += ' has-feedback';
8994 inputblock.cn.push(feedback);
8998 inputblock.cn.push({
9000 cls : 'input-group-addon',
9007 if (align ==='left' && this.fieldLabel.length) {
9008 // Roo.log("left and has label");
9014 cls : 'control-label col-sm-' + this.labelWidth,
9015 html : this.fieldLabel
9019 cls : "col-sm-" + (12 - this.labelWidth),
9026 } else if ( this.fieldLabel.length) {
9027 // Roo.log(" label");
9032 //cls : 'input-group-addon',
9033 html : this.fieldLabel
9043 // Roo.log(" no label && no align");
9053 if (this.disabled) {
9054 input.disabled=true;
9061 * return the real textarea element.
9063 inputEl: function ()
9065 return this.el.select('textarea.form-control',true).first();
9069 * Clear any invalid styles/messages for this field
9071 clearInvalid : function()
9074 if(!this.el || this.preventMark){ // not rendered
9078 var label = this.el.select('label', true).first();
9079 var icon = this.el.select('i.fa-star', true).first();
9085 this.el.removeClass(this.invalidClass);
9087 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9089 var feedback = this.el.select('.form-control-feedback', true).first();
9092 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9097 this.fireEvent('valid', this);
9101 * Mark this field as valid
9103 markValid : function()
9105 if(!this.el || this.preventMark){ // not rendered
9109 this.el.removeClass([this.invalidClass, this.validClass]);
9111 var feedback = this.el.select('.form-control-feedback', true).first();
9114 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9117 if(this.disabled || this.allowBlank){
9121 var label = this.el.select('label', true).first();
9122 var icon = this.el.select('i.fa-star', true).first();
9128 this.el.addClass(this.validClass);
9130 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9132 var feedback = this.el.select('.form-control-feedback', true).first();
9135 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9136 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9141 this.fireEvent('valid', this);
9145 * Mark this field as invalid
9146 * @param {String} msg The validation message
9148 markInvalid : function(msg)
9150 if(!this.el || this.preventMark){ // not rendered
9154 this.el.removeClass([this.invalidClass, this.validClass]);
9156 var feedback = this.el.select('.form-control-feedback', true).first();
9159 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9162 if(this.disabled || this.allowBlank){
9166 var label = this.el.select('label', true).first();
9167 var icon = this.el.select('i.fa-star', true).first();
9169 if(!this.getValue().length && label && !icon){
9170 this.el.createChild({
9172 cls : 'text-danger fa fa-lg fa-star',
9173 tooltip : 'This field is required',
9174 style : 'margin-right:5px;'
9178 this.el.addClass(this.invalidClass);
9180 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9182 var feedback = this.el.select('.form-control-feedback', true).first();
9185 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9187 if(this.getValue().length || this.forceFeedback){
9188 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9195 this.fireEvent('invalid', this, msg);
9203 * trigger field - base class for combo..
9208 * @class Roo.bootstrap.TriggerField
9209 * @extends Roo.bootstrap.Input
9210 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9211 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9212 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9213 * for which you can provide a custom implementation. For example:
9215 var trigger = new Roo.bootstrap.TriggerField();
9216 trigger.onTriggerClick = myTriggerFn;
9217 trigger.applyTo('my-field');
9220 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9221 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9222 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9223 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9224 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9227 * Create a new TriggerField.
9228 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9229 * to the base TextField)
9231 Roo.bootstrap.TriggerField = function(config){
9232 this.mimicing = false;
9233 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9236 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9238 * @cfg {String} triggerClass A CSS class to apply to the trigger
9241 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9246 * @cfg {Boolean} removable (true|false) special filter default false
9250 /** @cfg {Boolean} grow @hide */
9251 /** @cfg {Number} growMin @hide */
9252 /** @cfg {Number} growMax @hide */
9258 autoSize: Roo.emptyFn,
9265 actionMode : 'wrap',
9270 getAutoCreate : function(){
9272 var align = this.labelAlign || this.parentLabelAlign();
9277 cls: 'form-group' //input-group
9284 type : this.inputType,
9285 cls : 'form-control',
9286 autocomplete: 'new-password',
9287 placeholder : this.placeholder || ''
9291 input.name = this.name;
9294 input.cls += ' input-' + this.size;
9297 if (this.disabled) {
9298 input.disabled=true;
9301 var inputblock = input;
9303 if(this.hasFeedback && !this.allowBlank){
9307 cls: 'glyphicon form-control-feedback'
9310 if(this.removable && !this.editable && !this.tickable){
9312 cls : 'has-feedback',
9318 cls : 'roo-combo-removable-btn close'
9325 cls : 'has-feedback',
9334 if(this.removable && !this.editable && !this.tickable){
9336 cls : 'roo-removable',
9342 cls : 'roo-combo-removable-btn close'
9349 if (this.before || this.after) {
9352 cls : 'input-group',
9356 inputblock.cn.push({
9358 cls : 'input-group-addon',
9363 inputblock.cn.push(input);
9365 if(this.hasFeedback && !this.allowBlank){
9366 inputblock.cls += ' has-feedback';
9367 inputblock.cn.push(feedback);
9371 inputblock.cn.push({
9373 cls : 'input-group-addon',
9386 cls: 'form-hidden-field'
9400 cls: 'form-hidden-field'
9404 cls: 'roo-select2-choices',
9408 cls: 'roo-select2-search-field',
9421 cls: 'roo-select2-container input-group',
9426 // cls: 'typeahead typeahead-long dropdown-menu',
9427 // style: 'display:none'
9432 if(!this.multiple && this.showToggleBtn){
9438 if (this.caret != false) {
9441 cls: 'fa fa-' + this.caret
9448 cls : 'input-group-addon btn dropdown-toggle',
9453 cls: 'combobox-clear',
9467 combobox.cls += ' roo-select2-container-multi';
9470 if (align ==='left' && this.fieldLabel.length) {
9472 // Roo.log("left and has label");
9478 cls : 'control-label col-sm-' + this.labelWidth,
9479 html : this.fieldLabel
9483 cls : "col-sm-" + (12 - this.labelWidth),
9490 } else if ( this.fieldLabel.length) {
9491 // Roo.log(" label");
9496 //cls : 'input-group-addon',
9497 html : this.fieldLabel
9507 // Roo.log(" no label && no align");
9514 ['xs','sm','md','lg'].map(function(size){
9515 if (settings[size]) {
9516 cfg.cls += ' col-' + size + '-' + settings[size];
9527 onResize : function(w, h){
9528 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9529 // if(typeof w == 'number'){
9530 // var x = w - this.trigger.getWidth();
9531 // this.inputEl().setWidth(this.adjustWidth('input', x));
9532 // this.trigger.setStyle('left', x+'px');
9537 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9540 getResizeEl : function(){
9541 return this.inputEl();
9545 getPositionEl : function(){
9546 return this.inputEl();
9550 alignErrorIcon : function(){
9551 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9555 initEvents : function(){
9559 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9560 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9561 if(!this.multiple && this.showToggleBtn){
9562 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9563 if(this.hideTrigger){
9564 this.trigger.setDisplayed(false);
9566 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9570 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9573 if(this.removable && !this.editable && !this.tickable){
9574 var close = this.closeTriggerEl();
9577 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9578 close.on('click', this.removeBtnClick, this, close);
9582 //this.trigger.addClassOnOver('x-form-trigger-over');
9583 //this.trigger.addClassOnClick('x-form-trigger-click');
9586 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9590 closeTriggerEl : function()
9592 var close = this.el.select('.roo-combo-removable-btn', true).first();
9593 return close ? close : false;
9596 removeBtnClick : function(e, h, el)
9600 if(this.fireEvent("remove", this) !== false){
9602 this.fireEvent("afterremove", this)
9606 createList : function()
9608 this.list = Roo.get(document.body).createChild({
9610 cls: 'typeahead typeahead-long dropdown-menu',
9611 style: 'display:none'
9614 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9619 initTrigger : function(){
9624 onDestroy : function(){
9626 this.trigger.removeAllListeners();
9627 // this.trigger.remove();
9630 // this.wrap.remove();
9632 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9636 onFocus : function(){
9637 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9640 this.wrap.addClass('x-trigger-wrap-focus');
9641 this.mimicing = true;
9642 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9643 if(this.monitorTab){
9644 this.el.on("keydown", this.checkTab, this);
9651 checkTab : function(e){
9652 if(e.getKey() == e.TAB){
9658 onBlur : function(){
9663 mimicBlur : function(e, t){
9665 if(!this.wrap.contains(t) && this.validateBlur()){
9672 triggerBlur : function(){
9673 this.mimicing = false;
9674 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9675 if(this.monitorTab){
9676 this.el.un("keydown", this.checkTab, this);
9678 //this.wrap.removeClass('x-trigger-wrap-focus');
9679 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9683 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9684 validateBlur : function(e, t){
9689 onDisable : function(){
9690 this.inputEl().dom.disabled = true;
9691 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9693 // this.wrap.addClass('x-item-disabled');
9698 onEnable : function(){
9699 this.inputEl().dom.disabled = false;
9700 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9702 // this.el.removeClass('x-item-disabled');
9707 onShow : function(){
9708 var ae = this.getActionEl();
9711 ae.dom.style.display = '';
9712 ae.dom.style.visibility = 'visible';
9718 onHide : function(){
9719 var ae = this.getActionEl();
9720 ae.dom.style.display = 'none';
9724 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9725 * by an implementing function.
9727 * @param {EventObject} e
9729 onTriggerClick : Roo.emptyFn
9733 * Ext JS Library 1.1.1
9734 * Copyright(c) 2006-2007, Ext JS, LLC.
9736 * Originally Released Under LGPL - original licence link has changed is not relivant.
9739 * <script type="text/javascript">
9744 * @class Roo.data.SortTypes
9746 * Defines the default sorting (casting?) comparison functions used when sorting data.
9748 Roo.data.SortTypes = {
9750 * Default sort that does nothing
9751 * @param {Mixed} s The value being converted
9752 * @return {Mixed} The comparison value
9759 * The regular expression used to strip tags
9763 stripTagsRE : /<\/?[^>]+>/gi,
9766 * Strips all HTML tags to sort on text only
9767 * @param {Mixed} s The value being converted
9768 * @return {String} The comparison value
9770 asText : function(s){
9771 return String(s).replace(this.stripTagsRE, "");
9775 * Strips all HTML tags to sort on text only - Case insensitive
9776 * @param {Mixed} s The value being converted
9777 * @return {String} The comparison value
9779 asUCText : function(s){
9780 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9784 * Case insensitive string
9785 * @param {Mixed} s The value being converted
9786 * @return {String} The comparison value
9788 asUCString : function(s) {
9789 return String(s).toUpperCase();
9794 * @param {Mixed} s The value being converted
9795 * @return {Number} The comparison value
9797 asDate : function(s) {
9801 if(s instanceof Date){
9804 return Date.parse(String(s));
9809 * @param {Mixed} s The value being converted
9810 * @return {Float} The comparison value
9812 asFloat : function(s) {
9813 var val = parseFloat(String(s).replace(/,/g, ""));
9822 * @param {Mixed} s The value being converted
9823 * @return {Number} The comparison value
9825 asInt : function(s) {
9826 var val = parseInt(String(s).replace(/,/g, ""));
9834 * Ext JS Library 1.1.1
9835 * Copyright(c) 2006-2007, Ext JS, LLC.
9837 * Originally Released Under LGPL - original licence link has changed is not relivant.
9840 * <script type="text/javascript">
9844 * @class Roo.data.Record
9845 * Instances of this class encapsulate both record <em>definition</em> information, and record
9846 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9847 * to access Records cached in an {@link Roo.data.Store} object.<br>
9849 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9850 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9853 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9855 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9856 * {@link #create}. The parameters are the same.
9857 * @param {Array} data An associative Array of data values keyed by the field name.
9858 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9859 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9860 * not specified an integer id is generated.
9862 Roo.data.Record = function(data, id){
9863 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9868 * Generate a constructor for a specific record layout.
9869 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9870 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9871 * Each field definition object may contain the following properties: <ul>
9872 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
9873 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9874 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9875 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9876 * is being used, then this is a string containing the javascript expression to reference the data relative to
9877 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9878 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9879 * this may be omitted.</p></li>
9880 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9881 * <ul><li>auto (Default, implies no conversion)</li>
9886 * <li>date</li></ul></p></li>
9887 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9888 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9889 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9890 * by the Reader into an object that will be stored in the Record. It is passed the
9891 * following parameters:<ul>
9892 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9894 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9896 * <br>usage:<br><pre><code>
9897 var TopicRecord = Roo.data.Record.create(
9898 {name: 'title', mapping: 'topic_title'},
9899 {name: 'author', mapping: 'username'},
9900 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9901 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9902 {name: 'lastPoster', mapping: 'user2'},
9903 {name: 'excerpt', mapping: 'post_text'}
9906 var myNewRecord = new TopicRecord({
9907 title: 'Do my job please',
9910 lastPost: new Date(),
9911 lastPoster: 'Animal',
9912 excerpt: 'No way dude!'
9914 myStore.add(myNewRecord);
9919 Roo.data.Record.create = function(o){
9921 f.superclass.constructor.apply(this, arguments);
9923 Roo.extend(f, Roo.data.Record);
9924 var p = f.prototype;
9925 p.fields = new Roo.util.MixedCollection(false, function(field){
9928 for(var i = 0, len = o.length; i < len; i++){
9929 p.fields.add(new Roo.data.Field(o[i]));
9931 f.getField = function(name){
9932 return p.fields.get(name);
9937 Roo.data.Record.AUTO_ID = 1000;
9938 Roo.data.Record.EDIT = 'edit';
9939 Roo.data.Record.REJECT = 'reject';
9940 Roo.data.Record.COMMIT = 'commit';
9942 Roo.data.Record.prototype = {
9944 * Readonly flag - true if this record has been modified.
9953 join : function(store){
9958 * Set the named field to the specified value.
9959 * @param {String} name The name of the field to set.
9960 * @param {Object} value The value to set the field to.
9962 set : function(name, value){
9963 if(this.data[name] == value){
9970 if(typeof this.modified[name] == 'undefined'){
9971 this.modified[name] = this.data[name];
9973 this.data[name] = value;
9974 if(!this.editing && this.store){
9975 this.store.afterEdit(this);
9980 * Get the value of the named field.
9981 * @param {String} name The name of the field to get the value of.
9982 * @return {Object} The value of the field.
9984 get : function(name){
9985 return this.data[name];
9989 beginEdit : function(){
9990 this.editing = true;
9995 cancelEdit : function(){
9996 this.editing = false;
9997 delete this.modified;
10001 endEdit : function(){
10002 this.editing = false;
10003 if(this.dirty && this.store){
10004 this.store.afterEdit(this);
10009 * Usually called by the {@link Roo.data.Store} which owns the Record.
10010 * Rejects all changes made to the Record since either creation, or the last commit operation.
10011 * Modified fields are reverted to their original values.
10013 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10014 * of reject operations.
10016 reject : function(){
10017 var m = this.modified;
10019 if(typeof m[n] != "function"){
10020 this.data[n] = m[n];
10023 this.dirty = false;
10024 delete this.modified;
10025 this.editing = false;
10027 this.store.afterReject(this);
10032 * Usually called by the {@link Roo.data.Store} which owns the Record.
10033 * Commits all changes made to the Record since either creation, or the last commit operation.
10035 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10036 * of commit operations.
10038 commit : function(){
10039 this.dirty = false;
10040 delete this.modified;
10041 this.editing = false;
10043 this.store.afterCommit(this);
10048 hasError : function(){
10049 return this.error != null;
10053 clearError : function(){
10058 * Creates a copy of this record.
10059 * @param {String} id (optional) A new record id if you don't want to use this record's id
10062 copy : function(newId) {
10063 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10067 * Ext JS Library 1.1.1
10068 * Copyright(c) 2006-2007, Ext JS, LLC.
10070 * Originally Released Under LGPL - original licence link has changed is not relivant.
10073 * <script type="text/javascript">
10079 * @class Roo.data.Store
10080 * @extends Roo.util.Observable
10081 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10082 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10084 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
10085 * has no knowledge of the format of the data returned by the Proxy.<br>
10087 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10088 * instances from the data object. These records are cached and made available through accessor functions.
10090 * Creates a new Store.
10091 * @param {Object} config A config object containing the objects needed for the Store to access data,
10092 * and read the data into Records.
10094 Roo.data.Store = function(config){
10095 this.data = new Roo.util.MixedCollection(false);
10096 this.data.getKey = function(o){
10099 this.baseParams = {};
10101 this.paramNames = {
10106 "multisort" : "_multisort"
10109 if(config && config.data){
10110 this.inlineData = config.data;
10111 delete config.data;
10114 Roo.apply(this, config);
10116 if(this.reader){ // reader passed
10117 this.reader = Roo.factory(this.reader, Roo.data);
10118 this.reader.xmodule = this.xmodule || false;
10119 if(!this.recordType){
10120 this.recordType = this.reader.recordType;
10122 if(this.reader.onMetaChange){
10123 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10127 if(this.recordType){
10128 this.fields = this.recordType.prototype.fields;
10130 this.modified = [];
10134 * @event datachanged
10135 * Fires when the data cache has changed, and a widget which is using this Store
10136 * as a Record cache should refresh its view.
10137 * @param {Store} this
10139 datachanged : true,
10141 * @event metachange
10142 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10143 * @param {Store} this
10144 * @param {Object} meta The JSON metadata
10149 * Fires when Records have been added to the Store
10150 * @param {Store} this
10151 * @param {Roo.data.Record[]} records The array of Records added
10152 * @param {Number} index The index at which the record(s) were added
10157 * Fires when a Record has been removed from the Store
10158 * @param {Store} this
10159 * @param {Roo.data.Record} record The Record that was removed
10160 * @param {Number} index The index at which the record was removed
10165 * Fires when a Record has been updated
10166 * @param {Store} this
10167 * @param {Roo.data.Record} record The Record that was updated
10168 * @param {String} operation The update operation being performed. Value may be one of:
10170 Roo.data.Record.EDIT
10171 Roo.data.Record.REJECT
10172 Roo.data.Record.COMMIT
10178 * Fires when the data cache has been cleared.
10179 * @param {Store} this
10183 * @event beforeload
10184 * Fires before a request is made for a new data object. If the beforeload handler returns false
10185 * the load action will be canceled.
10186 * @param {Store} this
10187 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10191 * @event beforeloadadd
10192 * Fires after a new set of Records has been loaded.
10193 * @param {Store} this
10194 * @param {Roo.data.Record[]} records The Records that were loaded
10195 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10197 beforeloadadd : true,
10200 * Fires after a new set of Records has been loaded, before they are added to the store.
10201 * @param {Store} this
10202 * @param {Roo.data.Record[]} records The Records that were loaded
10203 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10204 * @params {Object} return from reader
10208 * @event loadexception
10209 * Fires if an exception occurs in the Proxy during loading.
10210 * Called with the signature of the Proxy's "loadexception" event.
10211 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10214 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10215 * @param {Object} load options
10216 * @param {Object} jsonData from your request (normally this contains the Exception)
10218 loadexception : true
10222 this.proxy = Roo.factory(this.proxy, Roo.data);
10223 this.proxy.xmodule = this.xmodule || false;
10224 this.relayEvents(this.proxy, ["loadexception"]);
10226 this.sortToggle = {};
10227 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10229 Roo.data.Store.superclass.constructor.call(this);
10231 if(this.inlineData){
10232 this.loadData(this.inlineData);
10233 delete this.inlineData;
10237 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10239 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10240 * without a remote query - used by combo/forms at present.
10244 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10247 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10250 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10251 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10254 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10255 * on any HTTP request
10258 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10261 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10265 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10266 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10268 remoteSort : false,
10271 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10272 * loaded or when a record is removed. (defaults to false).
10274 pruneModifiedRecords : false,
10277 lastOptions : null,
10280 * Add Records to the Store and fires the add event.
10281 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10283 add : function(records){
10284 records = [].concat(records);
10285 for(var i = 0, len = records.length; i < len; i++){
10286 records[i].join(this);
10288 var index = this.data.length;
10289 this.data.addAll(records);
10290 this.fireEvent("add", this, records, index);
10294 * Remove a Record from the Store and fires the remove event.
10295 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10297 remove : function(record){
10298 var index = this.data.indexOf(record);
10299 this.data.removeAt(index);
10300 if(this.pruneModifiedRecords){
10301 this.modified.remove(record);
10303 this.fireEvent("remove", this, record, index);
10307 * Remove all Records from the Store and fires the clear event.
10309 removeAll : function(){
10311 if(this.pruneModifiedRecords){
10312 this.modified = [];
10314 this.fireEvent("clear", this);
10318 * Inserts Records to the Store at the given index and fires the add event.
10319 * @param {Number} index The start index at which to insert the passed Records.
10320 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10322 insert : function(index, records){
10323 records = [].concat(records);
10324 for(var i = 0, len = records.length; i < len; i++){
10325 this.data.insert(index, records[i]);
10326 records[i].join(this);
10328 this.fireEvent("add", this, records, index);
10332 * Get the index within the cache of the passed Record.
10333 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10334 * @return {Number} The index of the passed Record. Returns -1 if not found.
10336 indexOf : function(record){
10337 return this.data.indexOf(record);
10341 * Get the index within the cache of the Record with the passed id.
10342 * @param {String} id The id of the Record to find.
10343 * @return {Number} The index of the Record. Returns -1 if not found.
10345 indexOfId : function(id){
10346 return this.data.indexOfKey(id);
10350 * Get the Record with the specified id.
10351 * @param {String} id The id of the Record to find.
10352 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10354 getById : function(id){
10355 return this.data.key(id);
10359 * Get the Record at the specified index.
10360 * @param {Number} index The index of the Record to find.
10361 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10363 getAt : function(index){
10364 return this.data.itemAt(index);
10368 * Returns a range of Records between specified indices.
10369 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10370 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10371 * @return {Roo.data.Record[]} An array of Records
10373 getRange : function(start, end){
10374 return this.data.getRange(start, end);
10378 storeOptions : function(o){
10379 o = Roo.apply({}, o);
10382 this.lastOptions = o;
10386 * Loads the Record cache from the configured Proxy using the configured Reader.
10388 * If using remote paging, then the first load call must specify the <em>start</em>
10389 * and <em>limit</em> properties in the options.params property to establish the initial
10390 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10392 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10393 * and this call will return before the new data has been loaded. Perform any post-processing
10394 * in a callback function, or in a "load" event handler.</strong>
10396 * @param {Object} options An object containing properties which control loading options:<ul>
10397 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10398 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10399 * passed the following arguments:<ul>
10400 * <li>r : Roo.data.Record[]</li>
10401 * <li>options: Options object from the load call</li>
10402 * <li>success: Boolean success indicator</li></ul></li>
10403 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10404 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10407 load : function(options){
10408 options = options || {};
10409 if(this.fireEvent("beforeload", this, options) !== false){
10410 this.storeOptions(options);
10411 var p = Roo.apply(options.params || {}, this.baseParams);
10412 // if meta was not loaded from remote source.. try requesting it.
10413 if (!this.reader.metaFromRemote) {
10414 p._requestMeta = 1;
10416 if(this.sortInfo && this.remoteSort){
10417 var pn = this.paramNames;
10418 p[pn["sort"]] = this.sortInfo.field;
10419 p[pn["dir"]] = this.sortInfo.direction;
10421 if (this.multiSort) {
10422 var pn = this.paramNames;
10423 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10426 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10431 * Reloads the Record cache from the configured Proxy using the configured Reader and
10432 * the options from the last load operation performed.
10433 * @param {Object} options (optional) An object containing properties which may override the options
10434 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10435 * the most recently used options are reused).
10437 reload : function(options){
10438 this.load(Roo.applyIf(options||{}, this.lastOptions));
10442 // Called as a callback by the Reader during a load operation.
10443 loadRecords : function(o, options, success){
10444 if(!o || success === false){
10445 if(success !== false){
10446 this.fireEvent("load", this, [], options, o);
10448 if(options.callback){
10449 options.callback.call(options.scope || this, [], options, false);
10453 // if data returned failure - throw an exception.
10454 if (o.success === false) {
10455 // show a message if no listener is registered.
10456 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10457 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10459 // loadmask wil be hooked into this..
10460 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10463 var r = o.records, t = o.totalRecords || r.length;
10465 this.fireEvent("beforeloadadd", this, r, options, o);
10467 if(!options || options.add !== true){
10468 if(this.pruneModifiedRecords){
10469 this.modified = [];
10471 for(var i = 0, len = r.length; i < len; i++){
10475 this.data = this.snapshot;
10476 delete this.snapshot;
10479 this.data.addAll(r);
10480 this.totalLength = t;
10482 this.fireEvent("datachanged", this);
10484 this.totalLength = Math.max(t, this.data.length+r.length);
10487 this.fireEvent("load", this, r, options, o);
10488 if(options.callback){
10489 options.callback.call(options.scope || this, r, options, true);
10495 * Loads data from a passed data block. A Reader which understands the format of the data
10496 * must have been configured in the constructor.
10497 * @param {Object} data The data block from which to read the Records. The format of the data expected
10498 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10499 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10501 loadData : function(o, append){
10502 var r = this.reader.readRecords(o);
10503 this.loadRecords(r, {add: append}, true);
10507 * Gets the number of cached records.
10509 * <em>If using paging, this may not be the total size of the dataset. If the data object
10510 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10511 * the data set size</em>
10513 getCount : function(){
10514 return this.data.length || 0;
10518 * Gets the total number of records in the dataset as returned by the server.
10520 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10521 * the dataset size</em>
10523 getTotalCount : function(){
10524 return this.totalLength || 0;
10528 * Returns the sort state of the Store as an object with two properties:
10530 field {String} The name of the field by which the Records are sorted
10531 direction {String} The sort order, "ASC" or "DESC"
10534 getSortState : function(){
10535 return this.sortInfo;
10539 applySort : function(){
10540 if(this.sortInfo && !this.remoteSort){
10541 var s = this.sortInfo, f = s.field;
10542 var st = this.fields.get(f).sortType;
10543 var fn = function(r1, r2){
10544 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10545 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10547 this.data.sort(s.direction, fn);
10548 if(this.snapshot && this.snapshot != this.data){
10549 this.snapshot.sort(s.direction, fn);
10555 * Sets the default sort column and order to be used by the next load operation.
10556 * @param {String} fieldName The name of the field to sort by.
10557 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10559 setDefaultSort : function(field, dir){
10560 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10564 * Sort the Records.
10565 * If remote sorting is used, the sort is performed on the server, and the cache is
10566 * reloaded. If local sorting is used, the cache is sorted internally.
10567 * @param {String} fieldName The name of the field to sort by.
10568 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10570 sort : function(fieldName, dir){
10571 var f = this.fields.get(fieldName);
10573 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10575 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10576 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10581 this.sortToggle[f.name] = dir;
10582 this.sortInfo = {field: f.name, direction: dir};
10583 if(!this.remoteSort){
10585 this.fireEvent("datachanged", this);
10587 this.load(this.lastOptions);
10592 * Calls the specified function for each of the Records in the cache.
10593 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10594 * Returning <em>false</em> aborts and exits the iteration.
10595 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10597 each : function(fn, scope){
10598 this.data.each(fn, scope);
10602 * Gets all records modified since the last commit. Modified records are persisted across load operations
10603 * (e.g., during paging).
10604 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10606 getModifiedRecords : function(){
10607 return this.modified;
10611 createFilterFn : function(property, value, anyMatch){
10612 if(!value.exec){ // not a regex
10613 value = String(value);
10614 if(value.length == 0){
10617 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10619 return function(r){
10620 return value.test(r.data[property]);
10625 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10626 * @param {String} property A field on your records
10627 * @param {Number} start The record index to start at (defaults to 0)
10628 * @param {Number} end The last record index to include (defaults to length - 1)
10629 * @return {Number} The sum
10631 sum : function(property, start, end){
10632 var rs = this.data.items, v = 0;
10633 start = start || 0;
10634 end = (end || end === 0) ? end : rs.length-1;
10636 for(var i = start; i <= end; i++){
10637 v += (rs[i].data[property] || 0);
10643 * Filter the records by a specified property.
10644 * @param {String} field A field on your records
10645 * @param {String/RegExp} value Either a string that the field
10646 * should start with or a RegExp to test against the field
10647 * @param {Boolean} anyMatch True to match any part not just the beginning
10649 filter : function(property, value, anyMatch){
10650 var fn = this.createFilterFn(property, value, anyMatch);
10651 return fn ? this.filterBy(fn) : this.clearFilter();
10655 * Filter by a function. The specified function will be called with each
10656 * record in this data source. If the function returns true the record is included,
10657 * otherwise it is filtered.
10658 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10659 * @param {Object} scope (optional) The scope of the function (defaults to this)
10661 filterBy : function(fn, scope){
10662 this.snapshot = this.snapshot || this.data;
10663 this.data = this.queryBy(fn, scope||this);
10664 this.fireEvent("datachanged", this);
10668 * Query the records by a specified property.
10669 * @param {String} field A field on your records
10670 * @param {String/RegExp} value Either a string that the field
10671 * should start with or a RegExp to test against the field
10672 * @param {Boolean} anyMatch True to match any part not just the beginning
10673 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10675 query : function(property, value, anyMatch){
10676 var fn = this.createFilterFn(property, value, anyMatch);
10677 return fn ? this.queryBy(fn) : this.data.clone();
10681 * Query by a function. The specified function will be called with each
10682 * record in this data source. If the function returns true the record is included
10684 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10685 * @param {Object} scope (optional) The scope of the function (defaults to this)
10686 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10688 queryBy : function(fn, scope){
10689 var data = this.snapshot || this.data;
10690 return data.filterBy(fn, scope||this);
10694 * Collects unique values for a particular dataIndex from this store.
10695 * @param {String} dataIndex The property to collect
10696 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10697 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10698 * @return {Array} An array of the unique values
10700 collect : function(dataIndex, allowNull, bypassFilter){
10701 var d = (bypassFilter === true && this.snapshot) ?
10702 this.snapshot.items : this.data.items;
10703 var v, sv, r = [], l = {};
10704 for(var i = 0, len = d.length; i < len; i++){
10705 v = d[i].data[dataIndex];
10707 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10716 * Revert to a view of the Record cache with no filtering applied.
10717 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10719 clearFilter : function(suppressEvent){
10720 if(this.snapshot && this.snapshot != this.data){
10721 this.data = this.snapshot;
10722 delete this.snapshot;
10723 if(suppressEvent !== true){
10724 this.fireEvent("datachanged", this);
10730 afterEdit : function(record){
10731 if(this.modified.indexOf(record) == -1){
10732 this.modified.push(record);
10734 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10738 afterReject : function(record){
10739 this.modified.remove(record);
10740 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10744 afterCommit : function(record){
10745 this.modified.remove(record);
10746 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10750 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10751 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10753 commitChanges : function(){
10754 var m = this.modified.slice(0);
10755 this.modified = [];
10756 for(var i = 0, len = m.length; i < len; i++){
10762 * Cancel outstanding changes on all changed records.
10764 rejectChanges : function(){
10765 var m = this.modified.slice(0);
10766 this.modified = [];
10767 for(var i = 0, len = m.length; i < len; i++){
10772 onMetaChange : function(meta, rtype, o){
10773 this.recordType = rtype;
10774 this.fields = rtype.prototype.fields;
10775 delete this.snapshot;
10776 this.sortInfo = meta.sortInfo || this.sortInfo;
10777 this.modified = [];
10778 this.fireEvent('metachange', this, this.reader.meta);
10781 moveIndex : function(data, type)
10783 var index = this.indexOf(data);
10785 var newIndex = index + type;
10789 this.insert(newIndex, data);
10794 * Ext JS Library 1.1.1
10795 * Copyright(c) 2006-2007, Ext JS, LLC.
10797 * Originally Released Under LGPL - original licence link has changed is not relivant.
10800 * <script type="text/javascript">
10804 * @class Roo.data.SimpleStore
10805 * @extends Roo.data.Store
10806 * Small helper class to make creating Stores from Array data easier.
10807 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10808 * @cfg {Array} fields An array of field definition objects, or field name strings.
10809 * @cfg {Array} data The multi-dimensional array of data
10811 * @param {Object} config
10813 Roo.data.SimpleStore = function(config){
10814 Roo.data.SimpleStore.superclass.constructor.call(this, {
10816 reader: new Roo.data.ArrayReader({
10819 Roo.data.Record.create(config.fields)
10821 proxy : new Roo.data.MemoryProxy(config.data)
10825 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10827 * Ext JS Library 1.1.1
10828 * Copyright(c) 2006-2007, Ext JS, LLC.
10830 * Originally Released Under LGPL - original licence link has changed is not relivant.
10833 * <script type="text/javascript">
10838 * @extends Roo.data.Store
10839 * @class Roo.data.JsonStore
10840 * Small helper class to make creating Stores for JSON data easier. <br/>
10842 var store = new Roo.data.JsonStore({
10843 url: 'get-images.php',
10845 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10848 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10849 * JsonReader and HttpProxy (unless inline data is provided).</b>
10850 * @cfg {Array} fields An array of field definition objects, or field name strings.
10852 * @param {Object} config
10854 Roo.data.JsonStore = function(c){
10855 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10856 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10857 reader: new Roo.data.JsonReader(c, c.fields)
10860 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10862 * Ext JS Library 1.1.1
10863 * Copyright(c) 2006-2007, Ext JS, LLC.
10865 * Originally Released Under LGPL - original licence link has changed is not relivant.
10868 * <script type="text/javascript">
10872 Roo.data.Field = function(config){
10873 if(typeof config == "string"){
10874 config = {name: config};
10876 Roo.apply(this, config);
10879 this.type = "auto";
10882 var st = Roo.data.SortTypes;
10883 // named sortTypes are supported, here we look them up
10884 if(typeof this.sortType == "string"){
10885 this.sortType = st[this.sortType];
10888 // set default sortType for strings and dates
10889 if(!this.sortType){
10892 this.sortType = st.asUCString;
10895 this.sortType = st.asDate;
10898 this.sortType = st.none;
10903 var stripRe = /[\$,%]/g;
10905 // prebuilt conversion function for this field, instead of
10906 // switching every time we're reading a value
10908 var cv, dateFormat = this.dateFormat;
10913 cv = function(v){ return v; };
10916 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10920 return v !== undefined && v !== null && v !== '' ?
10921 parseInt(String(v).replace(stripRe, ""), 10) : '';
10926 return v !== undefined && v !== null && v !== '' ?
10927 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10932 cv = function(v){ return v === true || v === "true" || v == 1; };
10939 if(v instanceof Date){
10943 if(dateFormat == "timestamp"){
10944 return new Date(v*1000);
10946 return Date.parseDate(v, dateFormat);
10948 var parsed = Date.parse(v);
10949 return parsed ? new Date(parsed) : null;
10958 Roo.data.Field.prototype = {
10966 * Ext JS Library 1.1.1
10967 * Copyright(c) 2006-2007, Ext JS, LLC.
10969 * Originally Released Under LGPL - original licence link has changed is not relivant.
10972 * <script type="text/javascript">
10975 // Base class for reading structured data from a data source. This class is intended to be
10976 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10979 * @class Roo.data.DataReader
10980 * Base class for reading structured data from a data source. This class is intended to be
10981 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10984 Roo.data.DataReader = function(meta, recordType){
10988 this.recordType = recordType instanceof Array ?
10989 Roo.data.Record.create(recordType) : recordType;
10992 Roo.data.DataReader.prototype = {
10994 * Create an empty record
10995 * @param {Object} data (optional) - overlay some values
10996 * @return {Roo.data.Record} record created.
10998 newRow : function(d) {
11000 this.recordType.prototype.fields.each(function(c) {
11002 case 'int' : da[c.name] = 0; break;
11003 case 'date' : da[c.name] = new Date(); break;
11004 case 'float' : da[c.name] = 0.0; break;
11005 case 'boolean' : da[c.name] = false; break;
11006 default : da[c.name] = ""; break;
11010 return new this.recordType(Roo.apply(da, d));
11015 * Ext JS Library 1.1.1
11016 * Copyright(c) 2006-2007, Ext JS, LLC.
11018 * Originally Released Under LGPL - original licence link has changed is not relivant.
11021 * <script type="text/javascript">
11025 * @class Roo.data.DataProxy
11026 * @extends Roo.data.Observable
11027 * This class is an abstract base class for implementations which provide retrieval of
11028 * unformatted data objects.<br>
11030 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11031 * (of the appropriate type which knows how to parse the data object) to provide a block of
11032 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11034 * Custom implementations must implement the load method as described in
11035 * {@link Roo.data.HttpProxy#load}.
11037 Roo.data.DataProxy = function(){
11040 * @event beforeload
11041 * Fires before a network request is made to retrieve a data object.
11042 * @param {Object} This DataProxy object.
11043 * @param {Object} params The params parameter to the load function.
11048 * Fires before the load method's callback is called.
11049 * @param {Object} This DataProxy object.
11050 * @param {Object} o The data object.
11051 * @param {Object} arg The callback argument object passed to the load function.
11055 * @event loadexception
11056 * Fires if an Exception occurs during data retrieval.
11057 * @param {Object} This DataProxy object.
11058 * @param {Object} o The data object.
11059 * @param {Object} arg The callback argument object passed to the load function.
11060 * @param {Object} e The Exception.
11062 loadexception : true
11064 Roo.data.DataProxy.superclass.constructor.call(this);
11067 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11070 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11074 * Ext JS Library 1.1.1
11075 * Copyright(c) 2006-2007, Ext JS, LLC.
11077 * Originally Released Under LGPL - original licence link has changed is not relivant.
11080 * <script type="text/javascript">
11083 * @class Roo.data.MemoryProxy
11084 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11085 * to the Reader when its load method is called.
11087 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11089 Roo.data.MemoryProxy = function(data){
11093 Roo.data.MemoryProxy.superclass.constructor.call(this);
11097 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11100 * Load data from the requested source (in this case an in-memory
11101 * data object passed to the constructor), read the data object into
11102 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11103 * process that block using the passed callback.
11104 * @param {Object} params This parameter is not used by the MemoryProxy class.
11105 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11106 * object into a block of Roo.data.Records.
11107 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11108 * The function must be passed <ul>
11109 * <li>The Record block object</li>
11110 * <li>The "arg" argument from the load function</li>
11111 * <li>A boolean success indicator</li>
11113 * @param {Object} scope The scope in which to call the callback
11114 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11116 load : function(params, reader, callback, scope, arg){
11117 params = params || {};
11120 result = reader.readRecords(this.data);
11122 this.fireEvent("loadexception", this, arg, null, e);
11123 callback.call(scope, null, arg, false);
11126 callback.call(scope, result, arg, true);
11130 update : function(params, records){
11135 * Ext JS Library 1.1.1
11136 * Copyright(c) 2006-2007, Ext JS, LLC.
11138 * Originally Released Under LGPL - original licence link has changed is not relivant.
11141 * <script type="text/javascript">
11144 * @class Roo.data.HttpProxy
11145 * @extends Roo.data.DataProxy
11146 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11147 * configured to reference a certain URL.<br><br>
11149 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11150 * from which the running page was served.<br><br>
11152 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11154 * Be aware that to enable the browser to parse an XML document, the server must set
11155 * the Content-Type header in the HTTP response to "text/xml".
11157 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11158 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11159 * will be used to make the request.
11161 Roo.data.HttpProxy = function(conn){
11162 Roo.data.HttpProxy.superclass.constructor.call(this);
11163 // is conn a conn config or a real conn?
11165 this.useAjax = !conn || !conn.events;
11169 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11170 // thse are take from connection...
11173 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11176 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11177 * extra parameters to each request made by this object. (defaults to undefined)
11180 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11181 * to each request made by this object. (defaults to undefined)
11184 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11187 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11190 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11196 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11200 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11201 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11202 * a finer-grained basis than the DataProxy events.
11204 getConnection : function(){
11205 return this.useAjax ? Roo.Ajax : this.conn;
11209 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11210 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11211 * process that block using the passed callback.
11212 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11213 * for the request to the remote server.
11214 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11215 * object into a block of Roo.data.Records.
11216 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11217 * The function must be passed <ul>
11218 * <li>The Record block object</li>
11219 * <li>The "arg" argument from the load function</li>
11220 * <li>A boolean success indicator</li>
11222 * @param {Object} scope The scope in which to call the callback
11223 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11225 load : function(params, reader, callback, scope, arg){
11226 if(this.fireEvent("beforeload", this, params) !== false){
11228 params : params || {},
11230 callback : callback,
11235 callback : this.loadResponse,
11239 Roo.applyIf(o, this.conn);
11240 if(this.activeRequest){
11241 Roo.Ajax.abort(this.activeRequest);
11243 this.activeRequest = Roo.Ajax.request(o);
11245 this.conn.request(o);
11248 callback.call(scope||this, null, arg, false);
11253 loadResponse : function(o, success, response){
11254 delete this.activeRequest;
11256 this.fireEvent("loadexception", this, o, response);
11257 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11262 result = o.reader.read(response);
11264 this.fireEvent("loadexception", this, o, response, e);
11265 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11269 this.fireEvent("load", this, o, o.request.arg);
11270 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11274 update : function(dataSet){
11279 updateResponse : function(dataSet){
11284 * Ext JS Library 1.1.1
11285 * Copyright(c) 2006-2007, Ext JS, LLC.
11287 * Originally Released Under LGPL - original licence link has changed is not relivant.
11290 * <script type="text/javascript">
11294 * @class Roo.data.ScriptTagProxy
11295 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11296 * other than the originating domain of the running page.<br><br>
11298 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
11299 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11301 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11302 * source code that is used as the source inside a <script> tag.<br><br>
11304 * In order for the browser to process the returned data, the server must wrap the data object
11305 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11306 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11307 * depending on whether the callback name was passed:
11310 boolean scriptTag = false;
11311 String cb = request.getParameter("callback");
11314 response.setContentType("text/javascript");
11316 response.setContentType("application/x-json");
11318 Writer out = response.getWriter();
11320 out.write(cb + "(");
11322 out.print(dataBlock.toJsonString());
11329 * @param {Object} config A configuration object.
11331 Roo.data.ScriptTagProxy = function(config){
11332 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11333 Roo.apply(this, config);
11334 this.head = document.getElementsByTagName("head")[0];
11337 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11339 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11341 * @cfg {String} url The URL from which to request the data object.
11344 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11348 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11349 * the server the name of the callback function set up by the load call to process the returned data object.
11350 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11351 * javascript output which calls this named function passing the data object as its only parameter.
11353 callbackParam : "callback",
11355 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11356 * name to the request.
11361 * Load data from the configured URL, read the data object into
11362 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11363 * process that block using the passed callback.
11364 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11365 * for the request to the remote server.
11366 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11367 * object into a block of Roo.data.Records.
11368 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11369 * The function must be passed <ul>
11370 * <li>The Record block object</li>
11371 * <li>The "arg" argument from the load function</li>
11372 * <li>A boolean success indicator</li>
11374 * @param {Object} scope The scope in which to call the callback
11375 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11377 load : function(params, reader, callback, scope, arg){
11378 if(this.fireEvent("beforeload", this, params) !== false){
11380 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11382 var url = this.url;
11383 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11385 url += "&_dc=" + (new Date().getTime());
11387 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11390 cb : "stcCallback"+transId,
11391 scriptId : "stcScript"+transId,
11395 callback : callback,
11401 window[trans.cb] = function(o){
11402 conn.handleResponse(o, trans);
11405 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11407 if(this.autoAbort !== false){
11411 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11413 var script = document.createElement("script");
11414 script.setAttribute("src", url);
11415 script.setAttribute("type", "text/javascript");
11416 script.setAttribute("id", trans.scriptId);
11417 this.head.appendChild(script);
11419 this.trans = trans;
11421 callback.call(scope||this, null, arg, false);
11426 isLoading : function(){
11427 return this.trans ? true : false;
11431 * Abort the current server request.
11433 abort : function(){
11434 if(this.isLoading()){
11435 this.destroyTrans(this.trans);
11440 destroyTrans : function(trans, isLoaded){
11441 this.head.removeChild(document.getElementById(trans.scriptId));
11442 clearTimeout(trans.timeoutId);
11444 window[trans.cb] = undefined;
11446 delete window[trans.cb];
11449 // if hasn't been loaded, wait for load to remove it to prevent script error
11450 window[trans.cb] = function(){
11451 window[trans.cb] = undefined;
11453 delete window[trans.cb];
11460 handleResponse : function(o, trans){
11461 this.trans = false;
11462 this.destroyTrans(trans, true);
11465 result = trans.reader.readRecords(o);
11467 this.fireEvent("loadexception", this, o, trans.arg, e);
11468 trans.callback.call(trans.scope||window, null, trans.arg, false);
11471 this.fireEvent("load", this, o, trans.arg);
11472 trans.callback.call(trans.scope||window, result, trans.arg, true);
11476 handleFailure : function(trans){
11477 this.trans = false;
11478 this.destroyTrans(trans, false);
11479 this.fireEvent("loadexception", this, null, trans.arg);
11480 trans.callback.call(trans.scope||window, null, trans.arg, false);
11484 * Ext JS Library 1.1.1
11485 * Copyright(c) 2006-2007, Ext JS, LLC.
11487 * Originally Released Under LGPL - original licence link has changed is not relivant.
11490 * <script type="text/javascript">
11494 * @class Roo.data.JsonReader
11495 * @extends Roo.data.DataReader
11496 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11497 * based on mappings in a provided Roo.data.Record constructor.
11499 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11500 * in the reply previously.
11505 var RecordDef = Roo.data.Record.create([
11506 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11507 {name: 'occupation'} // This field will use "occupation" as the mapping.
11509 var myReader = new Roo.data.JsonReader({
11510 totalProperty: "results", // The property which contains the total dataset size (optional)
11511 root: "rows", // The property which contains an Array of row objects
11512 id: "id" // The property within each row object that provides an ID for the record (optional)
11516 * This would consume a JSON file like this:
11518 { 'results': 2, 'rows': [
11519 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11520 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11523 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11524 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11525 * paged from the remote server.
11526 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11527 * @cfg {String} root name of the property which contains the Array of row objects.
11528 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11529 * @cfg {Array} fields Array of field definition objects
11531 * Create a new JsonReader
11532 * @param {Object} meta Metadata configuration options
11533 * @param {Object} recordType Either an Array of field definition objects,
11534 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11536 Roo.data.JsonReader = function(meta, recordType){
11539 // set some defaults:
11540 Roo.applyIf(meta, {
11541 totalProperty: 'total',
11542 successProperty : 'success',
11547 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11549 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11552 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11553 * Used by Store query builder to append _requestMeta to params.
11556 metaFromRemote : false,
11558 * This method is only used by a DataProxy which has retrieved data from a remote server.
11559 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11560 * @return {Object} data A data block which is used by an Roo.data.Store object as
11561 * a cache of Roo.data.Records.
11563 read : function(response){
11564 var json = response.responseText;
11566 var o = /* eval:var:o */ eval("("+json+")");
11568 throw {message: "JsonReader.read: Json object not found"};
11574 this.metaFromRemote = true;
11575 this.meta = o.metaData;
11576 this.recordType = Roo.data.Record.create(o.metaData.fields);
11577 this.onMetaChange(this.meta, this.recordType, o);
11579 return this.readRecords(o);
11582 // private function a store will implement
11583 onMetaChange : function(meta, recordType, o){
11590 simpleAccess: function(obj, subsc) {
11597 getJsonAccessor: function(){
11599 return function(expr) {
11601 return(re.test(expr))
11602 ? new Function("obj", "return obj." + expr)
11607 return Roo.emptyFn;
11612 * Create a data block containing Roo.data.Records from an XML document.
11613 * @param {Object} o An object which contains an Array of row objects in the property specified
11614 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11615 * which contains the total size of the dataset.
11616 * @return {Object} data A data block which is used by an Roo.data.Store object as
11617 * a cache of Roo.data.Records.
11619 readRecords : function(o){
11621 * After any data loads, the raw JSON data is available for further custom processing.
11625 var s = this.meta, Record = this.recordType,
11626 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11628 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11630 if(s.totalProperty) {
11631 this.getTotal = this.getJsonAccessor(s.totalProperty);
11633 if(s.successProperty) {
11634 this.getSuccess = this.getJsonAccessor(s.successProperty);
11636 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11638 var g = this.getJsonAccessor(s.id);
11639 this.getId = function(rec) {
11641 return (r === undefined || r === "") ? null : r;
11644 this.getId = function(){return null;};
11647 for(var jj = 0; jj < fl; jj++){
11649 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11650 this.ef[jj] = this.getJsonAccessor(map);
11654 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11655 if(s.totalProperty){
11656 var vt = parseInt(this.getTotal(o), 10);
11661 if(s.successProperty){
11662 var vs = this.getSuccess(o);
11663 if(vs === false || vs === 'false'){
11668 for(var i = 0; i < c; i++){
11671 var id = this.getId(n);
11672 for(var j = 0; j < fl; j++){
11674 var v = this.ef[j](n);
11676 Roo.log('missing convert for ' + f.name);
11680 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11682 var record = new Record(values, id);
11684 records[i] = record;
11690 totalRecords : totalRecords
11695 * Ext JS Library 1.1.1
11696 * Copyright(c) 2006-2007, Ext JS, LLC.
11698 * Originally Released Under LGPL - original licence link has changed is not relivant.
11701 * <script type="text/javascript">
11705 * @class Roo.data.ArrayReader
11706 * @extends Roo.data.DataReader
11707 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11708 * Each element of that Array represents a row of data fields. The
11709 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11710 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11714 var RecordDef = Roo.data.Record.create([
11715 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11716 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11718 var myReader = new Roo.data.ArrayReader({
11719 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11723 * This would consume an Array like this:
11725 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11727 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11729 * Create a new JsonReader
11730 * @param {Object} meta Metadata configuration options.
11731 * @param {Object} recordType Either an Array of field definition objects
11732 * as specified to {@link Roo.data.Record#create},
11733 * or an {@link Roo.data.Record} object
11734 * created using {@link Roo.data.Record#create}.
11736 Roo.data.ArrayReader = function(meta, recordType){
11737 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11740 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11742 * Create a data block containing Roo.data.Records from an XML document.
11743 * @param {Object} o An Array of row objects which represents the dataset.
11744 * @return {Object} data A data block which is used by an Roo.data.Store object as
11745 * a cache of Roo.data.Records.
11747 readRecords : function(o){
11748 var sid = this.meta ? this.meta.id : null;
11749 var recordType = this.recordType, fields = recordType.prototype.fields;
11752 for(var i = 0; i < root.length; i++){
11755 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11756 for(var j = 0, jlen = fields.length; j < jlen; j++){
11757 var f = fields.items[j];
11758 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11759 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11761 values[f.name] = v;
11763 var record = new recordType(values, id);
11765 records[records.length] = record;
11769 totalRecords : records.length
11778 * @class Roo.bootstrap.ComboBox
11779 * @extends Roo.bootstrap.TriggerField
11780 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11781 * @cfg {Boolean} append (true|false) default false
11782 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11783 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11784 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11785 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11786 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11787 * @cfg {Boolean} animate default true
11788 * @cfg {Boolean} emptyResultText only for touch device
11789 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11791 * Create a new ComboBox.
11792 * @param {Object} config Configuration options
11794 Roo.bootstrap.ComboBox = function(config){
11795 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11799 * Fires when the dropdown list is expanded
11800 * @param {Roo.bootstrap.ComboBox} combo This combo box
11805 * Fires when the dropdown list is collapsed
11806 * @param {Roo.bootstrap.ComboBox} combo This combo box
11810 * @event beforeselect
11811 * Fires before a list item is selected. Return false to cancel the selection.
11812 * @param {Roo.bootstrap.ComboBox} combo This combo box
11813 * @param {Roo.data.Record} record The data record returned from the underlying store
11814 * @param {Number} index The index of the selected item in the dropdown list
11816 'beforeselect' : true,
11819 * Fires when a list item is selected
11820 * @param {Roo.bootstrap.ComboBox} combo This combo box
11821 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11822 * @param {Number} index The index of the selected item in the dropdown list
11826 * @event beforequery
11827 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11828 * The event object passed has these properties:
11829 * @param {Roo.bootstrap.ComboBox} combo This combo box
11830 * @param {String} query The query
11831 * @param {Boolean} forceAll true to force "all" query
11832 * @param {Boolean} cancel true to cancel the query
11833 * @param {Object} e The query event object
11835 'beforequery': true,
11838 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11839 * @param {Roo.bootstrap.ComboBox} combo This combo box
11844 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11845 * @param {Roo.bootstrap.ComboBox} combo This combo box
11846 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11851 * Fires when the remove value from the combobox array
11852 * @param {Roo.bootstrap.ComboBox} combo This combo box
11856 * @event afterremove
11857 * Fires when the remove value from the combobox array
11858 * @param {Roo.bootstrap.ComboBox} combo This combo box
11860 'afterremove' : true,
11862 * @event specialfilter
11863 * Fires when specialfilter
11864 * @param {Roo.bootstrap.ComboBox} combo This combo box
11866 'specialfilter' : true,
11869 * Fires when tick the element
11870 * @param {Roo.bootstrap.ComboBox} combo This combo box
11874 * @event touchviewdisplay
11875 * Fires when touch view require special display (default is using displayField)
11876 * @param {Roo.bootstrap.ComboBox} combo This combo box
11877 * @param {Object} cfg set html .
11879 'touchviewdisplay' : true
11884 this.tickItems = [];
11886 this.selectedIndex = -1;
11887 if(this.mode == 'local'){
11888 if(config.queryDelay === undefined){
11889 this.queryDelay = 10;
11891 if(config.minChars === undefined){
11897 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11900 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11901 * rendering into an Roo.Editor, defaults to false)
11904 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11905 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11908 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11911 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11912 * the dropdown list (defaults to undefined, with no header element)
11916 * @cfg {String/Roo.Template} tpl The template to use to render the output
11920 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11922 listWidth: undefined,
11924 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11925 * mode = 'remote' or 'text' if mode = 'local')
11927 displayField: undefined,
11930 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11931 * mode = 'remote' or 'value' if mode = 'local').
11932 * Note: use of a valueField requires the user make a selection
11933 * in order for a value to be mapped.
11935 valueField: undefined,
11939 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11940 * field's data value (defaults to the underlying DOM element's name)
11942 hiddenName: undefined,
11944 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11948 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11950 selectedClass: 'active',
11953 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11957 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11958 * anchor positions (defaults to 'tl-bl')
11960 listAlign: 'tl-bl?',
11962 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11966 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11967 * query specified by the allQuery config option (defaults to 'query')
11969 triggerAction: 'query',
11971 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11972 * (defaults to 4, does not apply if editable = false)
11976 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11977 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11981 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11982 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11986 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11987 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11991 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11992 * when editable = true (defaults to false)
11994 selectOnFocus:false,
11996 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11998 queryParam: 'query',
12000 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12001 * when mode = 'remote' (defaults to 'Loading...')
12003 loadingText: 'Loading...',
12005 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12009 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12013 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12014 * traditional select (defaults to true)
12018 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12022 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12026 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12027 * listWidth has a higher value)
12031 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12032 * allow the user to set arbitrary text into the field (defaults to false)
12034 forceSelection:false,
12036 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12037 * if typeAhead = true (defaults to 250)
12039 typeAheadDelay : 250,
12041 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12042 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12044 valueNotFoundText : undefined,
12046 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12048 blockFocus : false,
12051 * @cfg {Boolean} disableClear Disable showing of clear button.
12053 disableClear : false,
12055 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12057 alwaysQuery : false,
12060 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12065 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12067 invalidClass : "has-warning",
12070 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12072 validClass : "has-success",
12075 * @cfg {Boolean} specialFilter (true|false) special filter default false
12077 specialFilter : false,
12080 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12082 mobileTouchView : true,
12094 btnPosition : 'right',
12095 triggerList : true,
12096 showToggleBtn : true,
12098 emptyResultText: 'Empty',
12099 triggerText : 'Select',
12101 // element that contains real text value.. (when hidden is used..)
12103 getAutoCreate : function()
12111 if(Roo.isTouch && this.mobileTouchView){
12112 cfg = this.getAutoCreateTouchView();
12119 if(!this.tickable){
12120 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12125 * ComboBox with tickable selections
12128 var align = this.labelAlign || this.parentLabelAlign();
12131 cls : 'form-group roo-combobox-tickable' //input-group
12136 cls : 'tickable-buttons',
12141 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12142 html : this.triggerText
12148 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12155 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12162 buttons.cn.unshift({
12164 cls: 'roo-select2-search-field-input'
12170 Roo.each(buttons.cn, function(c){
12172 c.cls += ' btn-' + _this.size;
12175 if (_this.disabled) {
12186 cls: 'form-hidden-field'
12190 cls: 'roo-select2-choices',
12194 cls: 'roo-select2-search-field',
12206 cls: 'roo-select2-container input-group roo-select2-container-multi',
12211 // cls: 'typeahead typeahead-long dropdown-menu',
12212 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12217 if(this.hasFeedback && !this.allowBlank){
12221 cls: 'glyphicon form-control-feedback'
12224 combobox.cn.push(feedback);
12227 if (align ==='left' && this.fieldLabel.length) {
12229 // Roo.log("left and has label");
12235 cls : 'control-label col-sm-' + this.labelWidth,
12236 html : this.fieldLabel
12240 cls : "col-sm-" + (12 - this.labelWidth),
12247 } else if ( this.fieldLabel.length) {
12248 // Roo.log(" label");
12253 //cls : 'input-group-addon',
12254 html : this.fieldLabel
12264 // Roo.log(" no label && no align");
12271 ['xs','sm','md','lg'].map(function(size){
12272 if (settings[size]) {
12273 cfg.cls += ' col-' + size + '-' + settings[size];
12281 _initEventsCalled : false,
12284 initEvents: function()
12287 if (this._initEventsCalled) { // as we call render... prevent looping...
12290 this._initEventsCalled = true;
12293 throw "can not find store for combo";
12296 this.store = Roo.factory(this.store, Roo.data);
12298 // if we are building from html. then this element is so complex, that we can not really
12299 // use the rendered HTML.
12300 // so we have to trash and replace the previous code.
12301 if (Roo.XComponent.build_from_html) {
12303 // remove this element....
12304 var e = this.el.dom, k=0;
12305 while (e ) { e = e.previousSibling; ++k;}
12310 this.rendered = false;
12312 this.render(this.parent().getChildContainer(true), k);
12323 if(Roo.isTouch && this.mobileTouchView){
12324 this.initTouchView();
12329 this.initTickableEvents();
12333 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12335 if(this.hiddenName){
12337 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12339 this.hiddenField.dom.value =
12340 this.hiddenValue !== undefined ? this.hiddenValue :
12341 this.value !== undefined ? this.value : '';
12343 // prevent input submission
12344 this.el.dom.removeAttribute('name');
12345 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12350 // this.el.dom.setAttribute('autocomplete', 'off');
12353 var cls = 'x-combo-list';
12355 //this.list = new Roo.Layer({
12356 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12362 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12363 _this.list.setWidth(lw);
12366 this.list.on('mouseover', this.onViewOver, this);
12367 this.list.on('mousemove', this.onViewMove, this);
12369 this.list.on('scroll', this.onViewScroll, this);
12372 this.list.swallowEvent('mousewheel');
12373 this.assetHeight = 0;
12376 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12377 this.assetHeight += this.header.getHeight();
12380 this.innerList = this.list.createChild({cls:cls+'-inner'});
12381 this.innerList.on('mouseover', this.onViewOver, this);
12382 this.innerList.on('mousemove', this.onViewMove, this);
12383 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12385 if(this.allowBlank && !this.pageSize && !this.disableClear){
12386 this.footer = this.list.createChild({cls:cls+'-ft'});
12387 this.pageTb = new Roo.Toolbar(this.footer);
12391 this.footer = this.list.createChild({cls:cls+'-ft'});
12392 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12393 {pageSize: this.pageSize});
12397 if (this.pageTb && this.allowBlank && !this.disableClear) {
12399 this.pageTb.add(new Roo.Toolbar.Fill(), {
12400 cls: 'x-btn-icon x-btn-clear',
12402 handler: function()
12405 _this.clearValue();
12406 _this.onSelect(false, -1);
12411 this.assetHeight += this.footer.getHeight();
12416 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12419 this.view = new Roo.View(this.list, this.tpl, {
12420 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12422 //this.view.wrapEl.setDisplayed(false);
12423 this.view.on('click', this.onViewClick, this);
12427 this.store.on('beforeload', this.onBeforeLoad, this);
12428 this.store.on('load', this.onLoad, this);
12429 this.store.on('loadexception', this.onLoadException, this);
12431 if(this.resizable){
12432 this.resizer = new Roo.Resizable(this.list, {
12433 pinned:true, handles:'se'
12435 this.resizer.on('resize', function(r, w, h){
12436 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12437 this.listWidth = w;
12438 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12439 this.restrictHeight();
12441 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12444 if(!this.editable){
12445 this.editable = true;
12446 this.setEditable(false);
12451 if (typeof(this.events.add.listeners) != 'undefined') {
12453 this.addicon = this.wrap.createChild(
12454 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12456 this.addicon.on('click', function(e) {
12457 this.fireEvent('add', this);
12460 if (typeof(this.events.edit.listeners) != 'undefined') {
12462 this.editicon = this.wrap.createChild(
12463 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12464 if (this.addicon) {
12465 this.editicon.setStyle('margin-left', '40px');
12467 this.editicon.on('click', function(e) {
12469 // we fire even if inothing is selected..
12470 this.fireEvent('edit', this, this.lastData );
12476 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12477 "up" : function(e){
12478 this.inKeyMode = true;
12482 "down" : function(e){
12483 if(!this.isExpanded()){
12484 this.onTriggerClick();
12486 this.inKeyMode = true;
12491 "enter" : function(e){
12492 // this.onViewClick();
12496 if(this.fireEvent("specialkey", this, e)){
12497 this.onViewClick(false);
12503 "esc" : function(e){
12507 "tab" : function(e){
12510 if(this.fireEvent("specialkey", this, e)){
12511 this.onViewClick(false);
12519 doRelay : function(foo, bar, hname){
12520 if(hname == 'down' || this.scope.isExpanded()){
12521 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12530 this.queryDelay = Math.max(this.queryDelay || 10,
12531 this.mode == 'local' ? 10 : 250);
12534 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12536 if(this.typeAhead){
12537 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12539 if(this.editable !== false){
12540 this.inputEl().on("keyup", this.onKeyUp, this);
12542 if(this.forceSelection){
12543 this.inputEl().on('blur', this.doForce, this);
12547 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12548 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12552 initTickableEvents: function()
12556 if(this.hiddenName){
12558 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12560 this.hiddenField.dom.value =
12561 this.hiddenValue !== undefined ? this.hiddenValue :
12562 this.value !== undefined ? this.value : '';
12564 // prevent input submission
12565 this.el.dom.removeAttribute('name');
12566 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12571 // this.list = this.el.select('ul.dropdown-menu',true).first();
12573 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12574 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12575 if(this.triggerList){
12576 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12579 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12580 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12582 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12583 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12585 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12586 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12588 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12589 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12590 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12593 this.cancelBtn.hide();
12598 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12599 _this.list.setWidth(lw);
12602 this.list.on('mouseover', this.onViewOver, this);
12603 this.list.on('mousemove', this.onViewMove, this);
12605 this.list.on('scroll', this.onViewScroll, this);
12608 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12611 this.view = new Roo.View(this.list, this.tpl, {
12612 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12615 //this.view.wrapEl.setDisplayed(false);
12616 this.view.on('click', this.onViewClick, this);
12620 this.store.on('beforeload', this.onBeforeLoad, this);
12621 this.store.on('load', this.onLoad, this);
12622 this.store.on('loadexception', this.onLoadException, this);
12625 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12626 "up" : function(e){
12627 this.inKeyMode = true;
12631 "down" : function(e){
12632 this.inKeyMode = true;
12636 "enter" : function(e){
12637 if(this.fireEvent("specialkey", this, e)){
12638 this.onViewClick(false);
12644 "esc" : function(e){
12645 this.onTickableFooterButtonClick(e, false, false);
12648 "tab" : function(e){
12649 this.fireEvent("specialkey", this, e);
12651 this.onTickableFooterButtonClick(e, false, false);
12658 doRelay : function(e, fn, key){
12659 if(this.scope.isExpanded()){
12660 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12669 this.queryDelay = Math.max(this.queryDelay || 10,
12670 this.mode == 'local' ? 10 : 250);
12673 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12675 if(this.typeAhead){
12676 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12679 if(this.editable !== false){
12680 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12685 onDestroy : function(){
12687 this.view.setStore(null);
12688 this.view.el.removeAllListeners();
12689 this.view.el.remove();
12690 this.view.purgeListeners();
12693 this.list.dom.innerHTML = '';
12697 this.store.un('beforeload', this.onBeforeLoad, this);
12698 this.store.un('load', this.onLoad, this);
12699 this.store.un('loadexception', this.onLoadException, this);
12701 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12705 fireKey : function(e){
12706 if(e.isNavKeyPress() && !this.list.isVisible()){
12707 this.fireEvent("specialkey", this, e);
12712 onResize: function(w, h){
12713 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12715 // if(typeof w != 'number'){
12716 // // we do not handle it!?!?
12719 // var tw = this.trigger.getWidth();
12720 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12721 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12723 // this.inputEl().setWidth( this.adjustWidth('input', x));
12725 // //this.trigger.setStyle('left', x+'px');
12727 // if(this.list && this.listWidth === undefined){
12728 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12729 // this.list.setWidth(lw);
12730 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12738 * Allow or prevent the user from directly editing the field text. If false is passed,
12739 * the user will only be able to select from the items defined in the dropdown list. This method
12740 * is the runtime equivalent of setting the 'editable' config option at config time.
12741 * @param {Boolean} value True to allow the user to directly edit the field text
12743 setEditable : function(value){
12744 if(value == this.editable){
12747 this.editable = value;
12749 this.inputEl().dom.setAttribute('readOnly', true);
12750 this.inputEl().on('mousedown', this.onTriggerClick, this);
12751 this.inputEl().addClass('x-combo-noedit');
12753 this.inputEl().dom.setAttribute('readOnly', false);
12754 this.inputEl().un('mousedown', this.onTriggerClick, this);
12755 this.inputEl().removeClass('x-combo-noedit');
12761 onBeforeLoad : function(combo,opts){
12762 if(!this.hasFocus){
12766 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12768 this.restrictHeight();
12769 this.selectedIndex = -1;
12773 onLoad : function(){
12775 this.hasQuery = false;
12777 if(!this.hasFocus){
12781 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12782 this.loading.hide();
12785 if(this.store.getCount() > 0){
12787 this.restrictHeight();
12788 if(this.lastQuery == this.allQuery){
12789 if(this.editable && !this.tickable){
12790 this.inputEl().dom.select();
12794 !this.selectByValue(this.value, true) &&
12797 !this.store.lastOptions ||
12798 typeof(this.store.lastOptions.add) == 'undefined' ||
12799 this.store.lastOptions.add != true
12802 this.select(0, true);
12805 if(this.autoFocus){
12808 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12809 this.taTask.delay(this.typeAheadDelay);
12813 this.onEmptyResults();
12819 onLoadException : function()
12821 this.hasQuery = false;
12823 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12824 this.loading.hide();
12827 if(this.tickable && this.editable){
12832 // only causes errors at present
12833 //Roo.log(this.store.reader.jsonData);
12834 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12836 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12842 onTypeAhead : function(){
12843 if(this.store.getCount() > 0){
12844 var r = this.store.getAt(0);
12845 var newValue = r.data[this.displayField];
12846 var len = newValue.length;
12847 var selStart = this.getRawValue().length;
12849 if(selStart != len){
12850 this.setRawValue(newValue);
12851 this.selectText(selStart, newValue.length);
12857 onSelect : function(record, index){
12859 if(this.fireEvent('beforeselect', this, record, index) !== false){
12861 this.setFromData(index > -1 ? record.data : false);
12864 this.fireEvent('select', this, record, index);
12869 * Returns the currently selected field value or empty string if no value is set.
12870 * @return {String} value The selected value
12872 getValue : function(){
12875 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12878 if(this.valueField){
12879 return typeof this.value != 'undefined' ? this.value : '';
12881 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12886 * Clears any text/value currently set in the field
12888 clearValue : function(){
12889 if(this.hiddenField){
12890 this.hiddenField.dom.value = '';
12893 this.setRawValue('');
12894 this.lastSelectionText = '';
12895 this.lastData = false;
12897 var close = this.closeTriggerEl();
12906 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12907 * will be displayed in the field. If the value does not match the data value of an existing item,
12908 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12909 * Otherwise the field will be blank (although the value will still be set).
12910 * @param {String} value The value to match
12912 setValue : function(v){
12919 if(this.valueField){
12920 var r = this.findRecord(this.valueField, v);
12922 text = r.data[this.displayField];
12923 }else if(this.valueNotFoundText !== undefined){
12924 text = this.valueNotFoundText;
12927 this.lastSelectionText = text;
12928 if(this.hiddenField){
12929 this.hiddenField.dom.value = v;
12931 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12934 var close = this.closeTriggerEl();
12937 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12941 * @property {Object} the last set data for the element
12946 * Sets the value of the field based on a object which is related to the record format for the store.
12947 * @param {Object} value the value to set as. or false on reset?
12949 setFromData : function(o){
12956 var dv = ''; // display value
12957 var vv = ''; // value value..
12959 if (this.displayField) {
12960 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12962 // this is an error condition!!!
12963 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12966 if(this.valueField){
12967 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12970 var close = this.closeTriggerEl();
12973 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12976 if(this.hiddenField){
12977 this.hiddenField.dom.value = vv;
12979 this.lastSelectionText = dv;
12980 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12984 // no hidden field.. - we store the value in 'value', but still display
12985 // display field!!!!
12986 this.lastSelectionText = dv;
12987 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12994 reset : function(){
12995 // overridden so that last data is reset..
13002 this.setValue(this.originalValue);
13003 this.clearInvalid();
13004 this.lastData = false;
13006 this.view.clearSelections();
13010 findRecord : function(prop, value){
13012 if(this.store.getCount() > 0){
13013 this.store.each(function(r){
13014 if(r.data[prop] == value){
13024 getName: function()
13026 // returns hidden if it's set..
13027 if (!this.rendered) {return ''};
13028 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13032 onViewMove : function(e, t){
13033 this.inKeyMode = false;
13037 onViewOver : function(e, t){
13038 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13041 var item = this.view.findItemFromChild(t);
13044 var index = this.view.indexOf(item);
13045 this.select(index, false);
13050 onViewClick : function(view, doFocus, el, e)
13052 var index = this.view.getSelectedIndexes()[0];
13054 var r = this.store.getAt(index);
13058 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13065 Roo.each(this.tickItems, function(v,k){
13067 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13069 _this.tickItems.splice(k, 1);
13071 if(typeof(e) == 'undefined' && view == false){
13072 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13084 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13085 this.tickItems.push(r.data);
13088 if(typeof(e) == 'undefined' && view == false){
13089 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13096 this.onSelect(r, index);
13098 if(doFocus !== false && !this.blockFocus){
13099 this.inputEl().focus();
13104 restrictHeight : function(){
13105 //this.innerList.dom.style.height = '';
13106 //var inner = this.innerList.dom;
13107 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13108 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13109 //this.list.beginUpdate();
13110 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13111 this.list.alignTo(this.inputEl(), this.listAlign);
13112 this.list.alignTo(this.inputEl(), this.listAlign);
13113 //this.list.endUpdate();
13117 onEmptyResults : function(){
13119 if(this.tickable && this.editable){
13120 this.restrictHeight();
13128 * Returns true if the dropdown list is expanded, else false.
13130 isExpanded : function(){
13131 return this.list.isVisible();
13135 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13136 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13137 * @param {String} value The data value of the item to select
13138 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13139 * selected item if it is not currently in view (defaults to true)
13140 * @return {Boolean} True if the value matched an item in the list, else false
13142 selectByValue : function(v, scrollIntoView){
13143 if(v !== undefined && v !== null){
13144 var r = this.findRecord(this.valueField || this.displayField, v);
13146 this.select(this.store.indexOf(r), scrollIntoView);
13154 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13155 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13156 * @param {Number} index The zero-based index of the list item to select
13157 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13158 * selected item if it is not currently in view (defaults to true)
13160 select : function(index, scrollIntoView){
13161 this.selectedIndex = index;
13162 this.view.select(index);
13163 if(scrollIntoView !== false){
13164 var el = this.view.getNode(index);
13166 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13169 this.list.scrollChildIntoView(el, false);
13175 selectNext : function(){
13176 var ct = this.store.getCount();
13178 if(this.selectedIndex == -1){
13180 }else if(this.selectedIndex < ct-1){
13181 this.select(this.selectedIndex+1);
13187 selectPrev : function(){
13188 var ct = this.store.getCount();
13190 if(this.selectedIndex == -1){
13192 }else if(this.selectedIndex != 0){
13193 this.select(this.selectedIndex-1);
13199 onKeyUp : function(e){
13200 if(this.editable !== false && !e.isSpecialKey()){
13201 this.lastKey = e.getKey();
13202 this.dqTask.delay(this.queryDelay);
13207 validateBlur : function(){
13208 return !this.list || !this.list.isVisible();
13212 initQuery : function(){
13214 var v = this.getRawValue();
13216 if(this.tickable && this.editable){
13217 v = this.tickableInputEl().getValue();
13224 doForce : function(){
13225 if(this.inputEl().dom.value.length > 0){
13226 this.inputEl().dom.value =
13227 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13233 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13234 * query allowing the query action to be canceled if needed.
13235 * @param {String} query The SQL query to execute
13236 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13237 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13238 * saved in the current store (defaults to false)
13240 doQuery : function(q, forceAll){
13242 if(q === undefined || q === null){
13247 forceAll: forceAll,
13251 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13256 forceAll = qe.forceAll;
13257 if(forceAll === true || (q.length >= this.minChars)){
13259 this.hasQuery = true;
13261 if(this.lastQuery != q || this.alwaysQuery){
13262 this.lastQuery = q;
13263 if(this.mode == 'local'){
13264 this.selectedIndex = -1;
13266 this.store.clearFilter();
13269 if(this.specialFilter){
13270 this.fireEvent('specialfilter', this);
13275 this.store.filter(this.displayField, q);
13278 this.store.fireEvent("datachanged", this.store);
13285 this.store.baseParams[this.queryParam] = q;
13287 var options = {params : this.getParams(q)};
13290 options.add = true;
13291 options.params.start = this.page * this.pageSize;
13294 this.store.load(options);
13297 * this code will make the page width larger, at the beginning, the list not align correctly,
13298 * we should expand the list on onLoad
13299 * so command out it
13304 this.selectedIndex = -1;
13309 this.loadNext = false;
13313 getParams : function(q){
13315 //p[this.queryParam] = q;
13319 p.limit = this.pageSize;
13325 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13327 collapse : function(){
13328 if(!this.isExpanded()){
13335 this.hasFocus = false;
13337 this.cancelBtn.hide();
13338 this.trigger.show();
13341 this.tickableInputEl().dom.value = '';
13342 this.tickableInputEl().blur();
13347 Roo.get(document).un('mousedown', this.collapseIf, this);
13348 Roo.get(document).un('mousewheel', this.collapseIf, this);
13349 if (!this.editable) {
13350 Roo.get(document).un('keydown', this.listKeyPress, this);
13352 this.fireEvent('collapse', this);
13356 collapseIf : function(e){
13357 var in_combo = e.within(this.el);
13358 var in_list = e.within(this.list);
13359 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13361 if (in_combo || in_list || is_list) {
13362 //e.stopPropagation();
13367 this.onTickableFooterButtonClick(e, false, false);
13375 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13377 expand : function(){
13379 if(this.isExpanded() || !this.hasFocus){
13383 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13384 this.list.setWidth(lw);
13391 this.restrictHeight();
13395 this.tickItems = Roo.apply([], this.item);
13398 this.cancelBtn.show();
13399 this.trigger.hide();
13402 this.tickableInputEl().focus();
13407 Roo.get(document).on('mousedown', this.collapseIf, this);
13408 Roo.get(document).on('mousewheel', this.collapseIf, this);
13409 if (!this.editable) {
13410 Roo.get(document).on('keydown', this.listKeyPress, this);
13413 this.fireEvent('expand', this);
13417 // Implements the default empty TriggerField.onTriggerClick function
13418 onTriggerClick : function(e)
13420 Roo.log('trigger click');
13422 if(this.disabled || !this.triggerList){
13427 this.loadNext = false;
13429 if(this.isExpanded()){
13431 if (!this.blockFocus) {
13432 this.inputEl().focus();
13436 this.hasFocus = true;
13437 if(this.triggerAction == 'all') {
13438 this.doQuery(this.allQuery, true);
13440 this.doQuery(this.getRawValue());
13442 if (!this.blockFocus) {
13443 this.inputEl().focus();
13448 onTickableTriggerClick : function(e)
13455 this.loadNext = false;
13456 this.hasFocus = true;
13458 if(this.triggerAction == 'all') {
13459 this.doQuery(this.allQuery, true);
13461 this.doQuery(this.getRawValue());
13465 onSearchFieldClick : function(e)
13467 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13468 this.onTickableFooterButtonClick(e, false, false);
13472 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13477 this.loadNext = false;
13478 this.hasFocus = true;
13480 if(this.triggerAction == 'all') {
13481 this.doQuery(this.allQuery, true);
13483 this.doQuery(this.getRawValue());
13487 listKeyPress : function(e)
13489 //Roo.log('listkeypress');
13490 // scroll to first matching element based on key pres..
13491 if (e.isSpecialKey()) {
13494 var k = String.fromCharCode(e.getKey()).toUpperCase();
13497 var csel = this.view.getSelectedNodes();
13498 var cselitem = false;
13500 var ix = this.view.indexOf(csel[0]);
13501 cselitem = this.store.getAt(ix);
13502 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13508 this.store.each(function(v) {
13510 // start at existing selection.
13511 if (cselitem.id == v.id) {
13517 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13518 match = this.store.indexOf(v);
13524 if (match === false) {
13525 return true; // no more action?
13528 this.view.select(match);
13529 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13530 sn.scrollIntoView(sn.dom.parentNode, false);
13533 onViewScroll : function(e, t){
13535 if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
13539 this.hasQuery = true;
13541 this.loading = this.list.select('.loading', true).first();
13543 if(this.loading === null){
13544 this.list.createChild({
13546 cls: 'loading roo-select2-more-results roo-select2-active',
13547 html: 'Loading more results...'
13550 this.loading = this.list.select('.loading', true).first();
13552 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13554 this.loading.hide();
13557 this.loading.show();
13562 this.loadNext = true;
13564 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13569 addItem : function(o)
13571 var dv = ''; // display value
13573 if (this.displayField) {
13574 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13576 // this is an error condition!!!
13577 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13584 var choice = this.choices.createChild({
13586 cls: 'roo-select2-search-choice',
13595 cls: 'roo-select2-search-choice-close',
13600 }, this.searchField);
13602 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13604 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13612 this.inputEl().dom.value = '';
13617 onRemoveItem : function(e, _self, o)
13619 e.preventDefault();
13621 this.lastItem = Roo.apply([], this.item);
13623 var index = this.item.indexOf(o.data) * 1;
13626 Roo.log('not this item?!');
13630 this.item.splice(index, 1);
13635 this.fireEvent('remove', this, e);
13641 syncValue : function()
13643 if(!this.item.length){
13650 Roo.each(this.item, function(i){
13651 if(_this.valueField){
13652 value.push(i[_this.valueField]);
13659 this.value = value.join(',');
13661 if(this.hiddenField){
13662 this.hiddenField.dom.value = this.value;
13665 this.store.fireEvent("datachanged", this.store);
13668 clearItem : function()
13670 if(!this.multiple){
13676 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13684 if(this.tickable && !Roo.isTouch){
13685 this.view.refresh();
13689 inputEl: function ()
13691 if(Roo.isTouch && this.mobileTouchView){
13692 return this.el.select('input.form-control',true).first();
13696 return this.searchField;
13699 return this.el.select('input.form-control',true).first();
13703 onTickableFooterButtonClick : function(e, btn, el)
13705 e.preventDefault();
13707 this.lastItem = Roo.apply([], this.item);
13709 if(btn && btn.name == 'cancel'){
13710 this.tickItems = Roo.apply([], this.item);
13719 Roo.each(this.tickItems, function(o){
13727 validate : function()
13729 var v = this.getRawValue();
13732 v = this.getValue();
13735 if(this.disabled || this.allowBlank || v.length){
13740 this.markInvalid();
13744 tickableInputEl : function()
13746 if(!this.tickable || !this.editable){
13747 return this.inputEl();
13750 return this.inputEl().select('.roo-select2-search-field-input', true).first();
13754 getAutoCreateTouchView : function()
13759 cls: 'form-group' //input-group
13765 type : this.inputType,
13766 cls : 'form-control x-combo-noedit',
13767 autocomplete: 'new-password',
13768 placeholder : this.placeholder || '',
13773 input.name = this.name;
13777 input.cls += ' input-' + this.size;
13780 if (this.disabled) {
13781 input.disabled = true;
13792 inputblock.cls += ' input-group';
13794 inputblock.cn.unshift({
13796 cls : 'input-group-addon',
13801 if(this.removable && !this.multiple){
13802 inputblock.cls += ' roo-removable';
13804 inputblock.cn.push({
13807 cls : 'roo-combo-removable-btn close'
13811 if(this.hasFeedback && !this.allowBlank){
13813 inputblock.cls += ' has-feedback';
13815 inputblock.cn.push({
13817 cls: 'glyphicon form-control-feedback'
13824 inputblock.cls += (this.before) ? '' : ' input-group';
13826 inputblock.cn.push({
13828 cls : 'input-group-addon',
13839 cls: 'form-hidden-field'
13853 cls: 'form-hidden-field'
13857 cls: 'roo-select2-choices',
13861 cls: 'roo-select2-search-field',
13874 cls: 'roo-select2-container input-group',
13881 combobox.cls += ' roo-select2-container-multi';
13884 var align = this.labelAlign || this.parentLabelAlign();
13888 if(this.fieldLabel.length){
13890 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13891 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13896 cls : 'control-label ' + lw,
13897 html : this.fieldLabel
13909 var settings = this;
13911 ['xs','sm','md','lg'].map(function(size){
13912 if (settings[size]) {
13913 cfg.cls += ' col-' + size + '-' + settings[size];
13920 initTouchView : function()
13922 this.renderTouchView();
13924 this.touchViewEl.on('scroll', function(){
13925 this.el.dom.scrollTop = 0;
13928 this.originalValue = this.getValue();
13930 this.inputEl().on("click", this.showTouchView, this);
13932 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13933 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13935 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13937 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13938 this.store.on('load', this.onTouchViewLoad, this);
13939 this.store.on('loadexception', this.onTouchViewLoadException, this);
13941 if(this.hiddenName){
13943 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13945 this.hiddenField.dom.value =
13946 this.hiddenValue !== undefined ? this.hiddenValue :
13947 this.value !== undefined ? this.value : '';
13949 this.el.dom.removeAttribute('name');
13950 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13954 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13955 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13958 if(this.removable && !this.multiple){
13959 var close = this.closeTriggerEl();
13961 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13962 close.on('click', this.removeBtnClick, this, close);
13966 * fix the bug in Safari iOS8
13968 this.inputEl().on("focus", function(e){
13969 document.activeElement.blur();
13977 renderTouchView : function()
13979 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13980 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13982 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13983 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13985 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13986 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13987 this.touchViewBodyEl.setStyle('overflow', 'auto');
13989 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13990 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13992 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13993 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13997 showTouchView : function()
14003 this.touchViewHeaderEl.hide();
14005 if(this.fieldLabel.length){
14006 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
14007 this.touchViewHeaderEl.show();
14010 this.touchViewEl.show();
14012 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14013 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14015 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14017 if(this.fieldLabel.length){
14018 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14021 this.touchViewBodyEl.setHeight(bodyHeight);
14025 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14027 this.touchViewEl.addClass('in');
14030 this.doTouchViewQuery();
14034 hideTouchView : function()
14036 this.touchViewEl.removeClass('in');
14040 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14042 this.touchViewEl.setStyle('display', 'none');
14047 setTouchViewValue : function()
14054 Roo.each(this.tickItems, function(o){
14059 this.hideTouchView();
14062 doTouchViewQuery : function()
14071 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14075 if(!this.alwaysQuery || this.mode == 'local'){
14076 this.onTouchViewLoad();
14083 onTouchViewBeforeLoad : function(combo,opts)
14089 onTouchViewLoad : function()
14091 if(this.store.getCount() < 1){
14092 this.onTouchViewEmptyResults();
14096 this.clearTouchView();
14098 var rawValue = this.getRawValue();
14100 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14102 this.tickItems = [];
14104 this.store.data.each(function(d, rowIndex){
14105 var row = this.touchViewListGroup.createChild(template);
14107 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14108 row.addClass(d.data.cls);
14111 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14114 html : d.data[this.displayField]
14117 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14118 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14122 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
14123 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14126 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
14127 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14128 this.tickItems.push(d.data);
14131 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14135 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14137 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14139 if(this.fieldLabel.length){
14140 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14143 var listHeight = this.touchViewListGroup.getHeight();
14147 if(firstChecked && listHeight > bodyHeight){
14148 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14153 onTouchViewLoadException : function()
14155 this.hideTouchView();
14158 onTouchViewEmptyResults : function()
14160 this.clearTouchView();
14162 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14164 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14168 clearTouchView : function()
14170 this.touchViewListGroup.dom.innerHTML = '';
14173 onTouchViewClick : function(e, el, o)
14175 e.preventDefault();
14178 var rowIndex = o.rowIndex;
14180 var r = this.store.getAt(rowIndex);
14182 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14184 if(!this.multiple){
14185 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14186 c.dom.removeAttribute('checked');
14189 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14191 this.setFromData(r.data);
14193 var close = this.closeTriggerEl();
14199 this.hideTouchView();
14201 this.fireEvent('select', this, r, rowIndex);
14206 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14207 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14208 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14212 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14213 this.addItem(r.data);
14214 this.tickItems.push(r.data);
14220 * @cfg {Boolean} grow
14224 * @cfg {Number} growMin
14228 * @cfg {Number} growMax
14237 Roo.apply(Roo.bootstrap.ComboBox, {
14241 cls: 'modal-header',
14263 cls: 'list-group-item',
14267 cls: 'roo-combobox-list-group-item-value'
14271 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14285 listItemCheckbox : {
14287 cls: 'list-group-item',
14291 cls: 'roo-combobox-list-group-item-value'
14295 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14311 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14316 cls: 'modal-footer',
14324 cls: 'col-xs-6 text-left',
14327 cls: 'btn btn-danger roo-touch-view-cancel',
14333 cls: 'col-xs-6 text-right',
14336 cls: 'btn btn-success roo-touch-view-ok',
14347 Roo.apply(Roo.bootstrap.ComboBox, {
14349 touchViewTemplate : {
14351 cls: 'modal fade roo-combobox-touch-view',
14355 cls: 'modal-dialog',
14356 style : 'position:fixed', // we have to fix position....
14360 cls: 'modal-content',
14362 Roo.bootstrap.ComboBox.header,
14363 Roo.bootstrap.ComboBox.body,
14364 Roo.bootstrap.ComboBox.footer
14373 * Ext JS Library 1.1.1
14374 * Copyright(c) 2006-2007, Ext JS, LLC.
14376 * Originally Released Under LGPL - original licence link has changed is not relivant.
14379 * <script type="text/javascript">
14384 * @extends Roo.util.Observable
14385 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14386 * This class also supports single and multi selection modes. <br>
14387 * Create a data model bound view:
14389 var store = new Roo.data.Store(...);
14391 var view = new Roo.View({
14393 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14395 singleSelect: true,
14396 selectedClass: "ydataview-selected",
14400 // listen for node click?
14401 view.on("click", function(vw, index, node, e){
14402 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14406 dataModel.load("foobar.xml");
14408 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14410 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14411 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14413 * Note: old style constructor is still suported (container, template, config)
14416 * Create a new View
14417 * @param {Object} config The config object
14420 Roo.View = function(config, depreciated_tpl, depreciated_config){
14422 this.parent = false;
14424 if (typeof(depreciated_tpl) == 'undefined') {
14425 // new way.. - universal constructor.
14426 Roo.apply(this, config);
14427 this.el = Roo.get(this.el);
14430 this.el = Roo.get(config);
14431 this.tpl = depreciated_tpl;
14432 Roo.apply(this, depreciated_config);
14434 this.wrapEl = this.el.wrap().wrap();
14435 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14438 if(typeof(this.tpl) == "string"){
14439 this.tpl = new Roo.Template(this.tpl);
14441 // support xtype ctors..
14442 this.tpl = new Roo.factory(this.tpl, Roo);
14446 this.tpl.compile();
14451 * @event beforeclick
14452 * Fires before a click is processed. Returns false to cancel the default action.
14453 * @param {Roo.View} this
14454 * @param {Number} index The index of the target node
14455 * @param {HTMLElement} node The target node
14456 * @param {Roo.EventObject} e The raw event object
14458 "beforeclick" : true,
14461 * Fires when a template node is clicked.
14462 * @param {Roo.View} this
14463 * @param {Number} index The index of the target node
14464 * @param {HTMLElement} node The target node
14465 * @param {Roo.EventObject} e The raw event object
14470 * Fires when a template node is double clicked.
14471 * @param {Roo.View} this
14472 * @param {Number} index The index of the target node
14473 * @param {HTMLElement} node The target node
14474 * @param {Roo.EventObject} e The raw event object
14478 * @event contextmenu
14479 * Fires when a template node is right clicked.
14480 * @param {Roo.View} this
14481 * @param {Number} index The index of the target node
14482 * @param {HTMLElement} node The target node
14483 * @param {Roo.EventObject} e The raw event object
14485 "contextmenu" : true,
14487 * @event selectionchange
14488 * Fires when the selected nodes change.
14489 * @param {Roo.View} this
14490 * @param {Array} selections Array of the selected nodes
14492 "selectionchange" : true,
14495 * @event beforeselect
14496 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14497 * @param {Roo.View} this
14498 * @param {HTMLElement} node The node to be selected
14499 * @param {Array} selections Array of currently selected nodes
14501 "beforeselect" : true,
14503 * @event preparedata
14504 * Fires on every row to render, to allow you to change the data.
14505 * @param {Roo.View} this
14506 * @param {Object} data to be rendered (change this)
14508 "preparedata" : true
14516 "click": this.onClick,
14517 "dblclick": this.onDblClick,
14518 "contextmenu": this.onContextMenu,
14522 this.selections = [];
14524 this.cmp = new Roo.CompositeElementLite([]);
14526 this.store = Roo.factory(this.store, Roo.data);
14527 this.setStore(this.store, true);
14530 if ( this.footer && this.footer.xtype) {
14532 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14534 this.footer.dataSource = this.store;
14535 this.footer.container = fctr;
14536 this.footer = Roo.factory(this.footer, Roo);
14537 fctr.insertFirst(this.el);
14539 // this is a bit insane - as the paging toolbar seems to detach the el..
14540 // dom.parentNode.parentNode.parentNode
14541 // they get detached?
14545 Roo.View.superclass.constructor.call(this);
14550 Roo.extend(Roo.View, Roo.util.Observable, {
14553 * @cfg {Roo.data.Store} store Data store to load data from.
14558 * @cfg {String|Roo.Element} el The container element.
14563 * @cfg {String|Roo.Template} tpl The template used by this View
14567 * @cfg {String} dataName the named area of the template to use as the data area
14568 * Works with domtemplates roo-name="name"
14572 * @cfg {String} selectedClass The css class to add to selected nodes
14574 selectedClass : "x-view-selected",
14576 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14581 * @cfg {String} text to display on mask (default Loading)
14585 * @cfg {Boolean} multiSelect Allow multiple selection
14587 multiSelect : false,
14589 * @cfg {Boolean} singleSelect Allow single selection
14591 singleSelect: false,
14594 * @cfg {Boolean} toggleSelect - selecting
14596 toggleSelect : false,
14599 * @cfg {Boolean} tickable - selecting
14604 * Returns the element this view is bound to.
14605 * @return {Roo.Element}
14607 getEl : function(){
14608 return this.wrapEl;
14614 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14616 refresh : function(){
14617 //Roo.log('refresh');
14620 // if we are using something like 'domtemplate', then
14621 // the what gets used is:
14622 // t.applySubtemplate(NAME, data, wrapping data..)
14623 // the outer template then get' applied with
14624 // the store 'extra data'
14625 // and the body get's added to the
14626 // roo-name="data" node?
14627 // <span class='roo-tpl-{name}'></span> ?????
14631 this.clearSelections();
14632 this.el.update("");
14634 var records = this.store.getRange();
14635 if(records.length < 1) {
14637 // is this valid?? = should it render a template??
14639 this.el.update(this.emptyText);
14643 if (this.dataName) {
14644 this.el.update(t.apply(this.store.meta)); //????
14645 el = this.el.child('.roo-tpl-' + this.dataName);
14648 for(var i = 0, len = records.length; i < len; i++){
14649 var data = this.prepareData(records[i].data, i, records[i]);
14650 this.fireEvent("preparedata", this, data, i, records[i]);
14652 var d = Roo.apply({}, data);
14655 Roo.apply(d, {'roo-id' : Roo.id()});
14659 Roo.each(this.parent.item, function(item){
14660 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14663 Roo.apply(d, {'roo-data-checked' : 'checked'});
14667 html[html.length] = Roo.util.Format.trim(
14669 t.applySubtemplate(this.dataName, d, this.store.meta) :
14676 el.update(html.join(""));
14677 this.nodes = el.dom.childNodes;
14678 this.updateIndexes(0);
14683 * Function to override to reformat the data that is sent to
14684 * the template for each node.
14685 * DEPRICATED - use the preparedata event handler.
14686 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14687 * a JSON object for an UpdateManager bound view).
14689 prepareData : function(data, index, record)
14691 this.fireEvent("preparedata", this, data, index, record);
14695 onUpdate : function(ds, record){
14696 // Roo.log('on update');
14697 this.clearSelections();
14698 var index = this.store.indexOf(record);
14699 var n = this.nodes[index];
14700 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14701 n.parentNode.removeChild(n);
14702 this.updateIndexes(index, index);
14708 onAdd : function(ds, records, index)
14710 //Roo.log(['on Add', ds, records, index] );
14711 this.clearSelections();
14712 if(this.nodes.length == 0){
14716 var n = this.nodes[index];
14717 for(var i = 0, len = records.length; i < len; i++){
14718 var d = this.prepareData(records[i].data, i, records[i]);
14720 this.tpl.insertBefore(n, d);
14723 this.tpl.append(this.el, d);
14726 this.updateIndexes(index);
14729 onRemove : function(ds, record, index){
14730 // Roo.log('onRemove');
14731 this.clearSelections();
14732 var el = this.dataName ?
14733 this.el.child('.roo-tpl-' + this.dataName) :
14736 el.dom.removeChild(this.nodes[index]);
14737 this.updateIndexes(index);
14741 * Refresh an individual node.
14742 * @param {Number} index
14744 refreshNode : function(index){
14745 this.onUpdate(this.store, this.store.getAt(index));
14748 updateIndexes : function(startIndex, endIndex){
14749 var ns = this.nodes;
14750 startIndex = startIndex || 0;
14751 endIndex = endIndex || ns.length - 1;
14752 for(var i = startIndex; i <= endIndex; i++){
14753 ns[i].nodeIndex = i;
14758 * Changes the data store this view uses and refresh the view.
14759 * @param {Store} store
14761 setStore : function(store, initial){
14762 if(!initial && this.store){
14763 this.store.un("datachanged", this.refresh);
14764 this.store.un("add", this.onAdd);
14765 this.store.un("remove", this.onRemove);
14766 this.store.un("update", this.onUpdate);
14767 this.store.un("clear", this.refresh);
14768 this.store.un("beforeload", this.onBeforeLoad);
14769 this.store.un("load", this.onLoad);
14770 this.store.un("loadexception", this.onLoad);
14774 store.on("datachanged", this.refresh, this);
14775 store.on("add", this.onAdd, this);
14776 store.on("remove", this.onRemove, this);
14777 store.on("update", this.onUpdate, this);
14778 store.on("clear", this.refresh, this);
14779 store.on("beforeload", this.onBeforeLoad, this);
14780 store.on("load", this.onLoad, this);
14781 store.on("loadexception", this.onLoad, this);
14789 * onbeforeLoad - masks the loading area.
14792 onBeforeLoad : function(store,opts)
14794 //Roo.log('onBeforeLoad');
14796 this.el.update("");
14798 this.el.mask(this.mask ? this.mask : "Loading" );
14800 onLoad : function ()
14807 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14808 * @param {HTMLElement} node
14809 * @return {HTMLElement} The template node
14811 findItemFromChild : function(node){
14812 var el = this.dataName ?
14813 this.el.child('.roo-tpl-' + this.dataName,true) :
14816 if(!node || node.parentNode == el){
14819 var p = node.parentNode;
14820 while(p && p != el){
14821 if(p.parentNode == el){
14830 onClick : function(e){
14831 var item = this.findItemFromChild(e.getTarget());
14833 var index = this.indexOf(item);
14834 if(this.onItemClick(item, index, e) !== false){
14835 this.fireEvent("click", this, index, item, e);
14838 this.clearSelections();
14843 onContextMenu : function(e){
14844 var item = this.findItemFromChild(e.getTarget());
14846 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14851 onDblClick : function(e){
14852 var item = this.findItemFromChild(e.getTarget());
14854 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14858 onItemClick : function(item, index, e)
14860 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14863 if (this.toggleSelect) {
14864 var m = this.isSelected(item) ? 'unselect' : 'select';
14867 _t[m](item, true, false);
14870 if(this.multiSelect || this.singleSelect){
14871 if(this.multiSelect && e.shiftKey && this.lastSelection){
14872 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14874 this.select(item, this.multiSelect && e.ctrlKey);
14875 this.lastSelection = item;
14878 if(!this.tickable){
14879 e.preventDefault();
14887 * Get the number of selected nodes.
14890 getSelectionCount : function(){
14891 return this.selections.length;
14895 * Get the currently selected nodes.
14896 * @return {Array} An array of HTMLElements
14898 getSelectedNodes : function(){
14899 return this.selections;
14903 * Get the indexes of the selected nodes.
14906 getSelectedIndexes : function(){
14907 var indexes = [], s = this.selections;
14908 for(var i = 0, len = s.length; i < len; i++){
14909 indexes.push(s[i].nodeIndex);
14915 * Clear all selections
14916 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14918 clearSelections : function(suppressEvent){
14919 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14920 this.cmp.elements = this.selections;
14921 this.cmp.removeClass(this.selectedClass);
14922 this.selections = [];
14923 if(!suppressEvent){
14924 this.fireEvent("selectionchange", this, this.selections);
14930 * Returns true if the passed node is selected
14931 * @param {HTMLElement/Number} node The node or node index
14932 * @return {Boolean}
14934 isSelected : function(node){
14935 var s = this.selections;
14939 node = this.getNode(node);
14940 return s.indexOf(node) !== -1;
14945 * @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
14946 * @param {Boolean} keepExisting (optional) true to keep existing selections
14947 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14949 select : function(nodeInfo, keepExisting, suppressEvent){
14950 if(nodeInfo instanceof Array){
14952 this.clearSelections(true);
14954 for(var i = 0, len = nodeInfo.length; i < len; i++){
14955 this.select(nodeInfo[i], true, true);
14959 var node = this.getNode(nodeInfo);
14960 if(!node || this.isSelected(node)){
14961 return; // already selected.
14964 this.clearSelections(true);
14967 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14968 Roo.fly(node).addClass(this.selectedClass);
14969 this.selections.push(node);
14970 if(!suppressEvent){
14971 this.fireEvent("selectionchange", this, this.selections);
14979 * @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
14980 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14981 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14983 unselect : function(nodeInfo, keepExisting, suppressEvent)
14985 if(nodeInfo instanceof Array){
14986 Roo.each(this.selections, function(s) {
14987 this.unselect(s, nodeInfo);
14991 var node = this.getNode(nodeInfo);
14992 if(!node || !this.isSelected(node)){
14993 //Roo.log("not selected");
14994 return; // not selected.
14998 Roo.each(this.selections, function(s) {
15000 Roo.fly(node).removeClass(this.selectedClass);
15007 this.selections= ns;
15008 this.fireEvent("selectionchange", this, this.selections);
15012 * Gets a template node.
15013 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15014 * @return {HTMLElement} The node or null if it wasn't found
15016 getNode : function(nodeInfo){
15017 if(typeof nodeInfo == "string"){
15018 return document.getElementById(nodeInfo);
15019 }else if(typeof nodeInfo == "number"){
15020 return this.nodes[nodeInfo];
15026 * Gets a range template nodes.
15027 * @param {Number} startIndex
15028 * @param {Number} endIndex
15029 * @return {Array} An array of nodes
15031 getNodes : function(start, end){
15032 var ns = this.nodes;
15033 start = start || 0;
15034 end = typeof end == "undefined" ? ns.length - 1 : end;
15037 for(var i = start; i <= end; i++){
15041 for(var i = start; i >= end; i--){
15049 * Finds the index of the passed node
15050 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15051 * @return {Number} The index of the node or -1
15053 indexOf : function(node){
15054 node = this.getNode(node);
15055 if(typeof node.nodeIndex == "number"){
15056 return node.nodeIndex;
15058 var ns = this.nodes;
15059 for(var i = 0, len = ns.length; i < len; i++){
15070 * based on jquery fullcalendar
15074 Roo.bootstrap = Roo.bootstrap || {};
15076 * @class Roo.bootstrap.Calendar
15077 * @extends Roo.bootstrap.Component
15078 * Bootstrap Calendar class
15079 * @cfg {Boolean} loadMask (true|false) default false
15080 * @cfg {Object} header generate the user specific header of the calendar, default false
15083 * Create a new Container
15084 * @param {Object} config The config object
15089 Roo.bootstrap.Calendar = function(config){
15090 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15094 * Fires when a date is selected
15095 * @param {DatePicker} this
15096 * @param {Date} date The selected date
15100 * @event monthchange
15101 * Fires when the displayed month changes
15102 * @param {DatePicker} this
15103 * @param {Date} date The selected month
15105 'monthchange': true,
15107 * @event evententer
15108 * Fires when mouse over an event
15109 * @param {Calendar} this
15110 * @param {event} Event
15112 'evententer': true,
15114 * @event eventleave
15115 * Fires when the mouse leaves an
15116 * @param {Calendar} this
15119 'eventleave': true,
15121 * @event eventclick
15122 * Fires when the mouse click an
15123 * @param {Calendar} this
15132 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15135 * @cfg {Number} startDay
15136 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15144 getAutoCreate : function(){
15147 var fc_button = function(name, corner, style, content ) {
15148 return Roo.apply({},{
15150 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15152 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15155 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15166 style : 'width:100%',
15173 cls : 'fc-header-left',
15175 fc_button('prev', 'left', 'arrow', '‹' ),
15176 fc_button('next', 'right', 'arrow', '›' ),
15177 { tag: 'span', cls: 'fc-header-space' },
15178 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15186 cls : 'fc-header-center',
15190 cls: 'fc-header-title',
15193 html : 'month / year'
15201 cls : 'fc-header-right',
15203 /* fc_button('month', 'left', '', 'month' ),
15204 fc_button('week', '', '', 'week' ),
15205 fc_button('day', 'right', '', 'day' )
15217 header = this.header;
15220 var cal_heads = function() {
15222 // fixme - handle this.
15224 for (var i =0; i < Date.dayNames.length; i++) {
15225 var d = Date.dayNames[i];
15228 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15229 html : d.substring(0,3)
15233 ret[0].cls += ' fc-first';
15234 ret[6].cls += ' fc-last';
15237 var cal_cell = function(n) {
15240 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15245 cls: 'fc-day-number',
15249 cls: 'fc-day-content',
15253 style: 'position: relative;' // height: 17px;
15265 var cal_rows = function() {
15268 for (var r = 0; r < 6; r++) {
15275 for (var i =0; i < Date.dayNames.length; i++) {
15276 var d = Date.dayNames[i];
15277 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15280 row.cn[0].cls+=' fc-first';
15281 row.cn[0].cn[0].style = 'min-height:90px';
15282 row.cn[6].cls+=' fc-last';
15286 ret[0].cls += ' fc-first';
15287 ret[4].cls += ' fc-prev-last';
15288 ret[5].cls += ' fc-last';
15295 cls: 'fc-border-separate',
15296 style : 'width:100%',
15304 cls : 'fc-first fc-last',
15322 cls : 'fc-content',
15323 style : "position: relative;",
15326 cls : 'fc-view fc-view-month fc-grid',
15327 style : 'position: relative',
15328 unselectable : 'on',
15331 cls : 'fc-event-container',
15332 style : 'position:absolute;z-index:8;top:0;left:0;'
15350 initEvents : function()
15353 throw "can not find store for calendar";
15359 style: "text-align:center",
15363 style: "background-color:white;width:50%;margin:250 auto",
15367 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15378 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15380 var size = this.el.select('.fc-content', true).first().getSize();
15381 this.maskEl.setSize(size.width, size.height);
15382 this.maskEl.enableDisplayMode("block");
15383 if(!this.loadMask){
15384 this.maskEl.hide();
15387 this.store = Roo.factory(this.store, Roo.data);
15388 this.store.on('load', this.onLoad, this);
15389 this.store.on('beforeload', this.onBeforeLoad, this);
15393 this.cells = this.el.select('.fc-day',true);
15394 //Roo.log(this.cells);
15395 this.textNodes = this.el.query('.fc-day-number');
15396 this.cells.addClassOnOver('fc-state-hover');
15398 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15399 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15400 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15401 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15403 this.on('monthchange', this.onMonthChange, this);
15405 this.update(new Date().clearTime());
15408 resize : function() {
15409 var sz = this.el.getSize();
15411 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15412 this.el.select('.fc-day-content div',true).setHeight(34);
15417 showPrevMonth : function(e){
15418 this.update(this.activeDate.add("mo", -1));
15420 showToday : function(e){
15421 this.update(new Date().clearTime());
15424 showNextMonth : function(e){
15425 this.update(this.activeDate.add("mo", 1));
15429 showPrevYear : function(){
15430 this.update(this.activeDate.add("y", -1));
15434 showNextYear : function(){
15435 this.update(this.activeDate.add("y", 1));
15440 update : function(date)
15442 var vd = this.activeDate;
15443 this.activeDate = date;
15444 // if(vd && this.el){
15445 // var t = date.getTime();
15446 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15447 // Roo.log('using add remove');
15449 // this.fireEvent('monthchange', this, date);
15451 // this.cells.removeClass("fc-state-highlight");
15452 // this.cells.each(function(c){
15453 // if(c.dateValue == t){
15454 // c.addClass("fc-state-highlight");
15455 // setTimeout(function(){
15456 // try{c.dom.firstChild.focus();}catch(e){}
15466 var days = date.getDaysInMonth();
15468 var firstOfMonth = date.getFirstDateOfMonth();
15469 var startingPos = firstOfMonth.getDay()-this.startDay;
15471 if(startingPos < this.startDay){
15475 var pm = date.add(Date.MONTH, -1);
15476 var prevStart = pm.getDaysInMonth()-startingPos;
15478 this.cells = this.el.select('.fc-day',true);
15479 this.textNodes = this.el.query('.fc-day-number');
15480 this.cells.addClassOnOver('fc-state-hover');
15482 var cells = this.cells.elements;
15483 var textEls = this.textNodes;
15485 Roo.each(cells, function(cell){
15486 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15489 days += startingPos;
15491 // convert everything to numbers so it's fast
15492 var day = 86400000;
15493 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15496 //Roo.log(prevStart);
15498 var today = new Date().clearTime().getTime();
15499 var sel = date.clearTime().getTime();
15500 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15501 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15502 var ddMatch = this.disabledDatesRE;
15503 var ddText = this.disabledDatesText;
15504 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15505 var ddaysText = this.disabledDaysText;
15506 var format = this.format;
15508 var setCellClass = function(cal, cell){
15512 //Roo.log('set Cell Class');
15514 var t = d.getTime();
15518 cell.dateValue = t;
15520 cell.className += " fc-today";
15521 cell.className += " fc-state-highlight";
15522 cell.title = cal.todayText;
15525 // disable highlight in other month..
15526 //cell.className += " fc-state-highlight";
15531 cell.className = " fc-state-disabled";
15532 cell.title = cal.minText;
15536 cell.className = " fc-state-disabled";
15537 cell.title = cal.maxText;
15541 if(ddays.indexOf(d.getDay()) != -1){
15542 cell.title = ddaysText;
15543 cell.className = " fc-state-disabled";
15546 if(ddMatch && format){
15547 var fvalue = d.dateFormat(format);
15548 if(ddMatch.test(fvalue)){
15549 cell.title = ddText.replace("%0", fvalue);
15550 cell.className = " fc-state-disabled";
15554 if (!cell.initialClassName) {
15555 cell.initialClassName = cell.dom.className;
15558 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15563 for(; i < startingPos; i++) {
15564 textEls[i].innerHTML = (++prevStart);
15565 d.setDate(d.getDate()+1);
15567 cells[i].className = "fc-past fc-other-month";
15568 setCellClass(this, cells[i]);
15573 for(; i < days; i++){
15574 intDay = i - startingPos + 1;
15575 textEls[i].innerHTML = (intDay);
15576 d.setDate(d.getDate()+1);
15578 cells[i].className = ''; // "x-date-active";
15579 setCellClass(this, cells[i]);
15583 for(; i < 42; i++) {
15584 textEls[i].innerHTML = (++extraDays);
15585 d.setDate(d.getDate()+1);
15587 cells[i].className = "fc-future fc-other-month";
15588 setCellClass(this, cells[i]);
15591 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15593 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15595 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15596 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15598 if(totalRows != 6){
15599 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15600 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15603 this.fireEvent('monthchange', this, date);
15607 if(!this.internalRender){
15608 var main = this.el.dom.firstChild;
15609 var w = main.offsetWidth;
15610 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15611 Roo.fly(main).setWidth(w);
15612 this.internalRender = true;
15613 // opera does not respect the auto grow header center column
15614 // then, after it gets a width opera refuses to recalculate
15615 // without a second pass
15616 if(Roo.isOpera && !this.secondPass){
15617 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15618 this.secondPass = true;
15619 this.update.defer(10, this, [date]);
15626 findCell : function(dt) {
15627 dt = dt.clearTime().getTime();
15629 this.cells.each(function(c){
15630 //Roo.log("check " +c.dateValue + '?=' + dt);
15631 if(c.dateValue == dt){
15641 findCells : function(ev) {
15642 var s = ev.start.clone().clearTime().getTime();
15644 var e= ev.end.clone().clearTime().getTime();
15647 this.cells.each(function(c){
15648 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15650 if(c.dateValue > e){
15653 if(c.dateValue < s){
15662 // findBestRow: function(cells)
15666 // for (var i =0 ; i < cells.length;i++) {
15667 // ret = Math.max(cells[i].rows || 0,ret);
15674 addItem : function(ev)
15676 // look for vertical location slot in
15677 var cells = this.findCells(ev);
15679 // ev.row = this.findBestRow(cells);
15681 // work out the location.
15685 for(var i =0; i < cells.length; i++) {
15687 cells[i].row = cells[0].row;
15690 cells[i].row = cells[i].row + 1;
15700 if (crow.start.getY() == cells[i].getY()) {
15702 crow.end = cells[i];
15719 cells[0].events.push(ev);
15721 this.calevents.push(ev);
15724 clearEvents: function() {
15726 if(!this.calevents){
15730 Roo.each(this.cells.elements, function(c){
15736 Roo.each(this.calevents, function(e) {
15737 Roo.each(e.els, function(el) {
15738 el.un('mouseenter' ,this.onEventEnter, this);
15739 el.un('mouseleave' ,this.onEventLeave, this);
15744 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15750 renderEvents: function()
15754 this.cells.each(function(c) {
15763 if(c.row != c.events.length){
15764 r = 4 - (4 - (c.row - c.events.length));
15767 c.events = ev.slice(0, r);
15768 c.more = ev.slice(r);
15770 if(c.more.length && c.more.length == 1){
15771 c.events.push(c.more.pop());
15774 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15778 this.cells.each(function(c) {
15780 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15783 for (var e = 0; e < c.events.length; e++){
15784 var ev = c.events[e];
15785 var rows = ev.rows;
15787 for(var i = 0; i < rows.length; i++) {
15789 // how many rows should it span..
15792 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15793 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15795 unselectable : "on",
15798 cls: 'fc-event-inner',
15802 // cls: 'fc-event-time',
15803 // html : cells.length > 1 ? '' : ev.time
15807 cls: 'fc-event-title',
15808 html : String.format('{0}', ev.title)
15815 cls: 'ui-resizable-handle ui-resizable-e',
15816 html : '  '
15823 cfg.cls += ' fc-event-start';
15825 if ((i+1) == rows.length) {
15826 cfg.cls += ' fc-event-end';
15829 var ctr = _this.el.select('.fc-event-container',true).first();
15830 var cg = ctr.createChild(cfg);
15832 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15833 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15835 var r = (c.more.length) ? 1 : 0;
15836 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15837 cg.setWidth(ebox.right - sbox.x -2);
15839 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15840 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15841 cg.on('click', _this.onEventClick, _this, ev);
15852 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15853 style : 'position: absolute',
15854 unselectable : "on",
15857 cls: 'fc-event-inner',
15861 cls: 'fc-event-title',
15869 cls: 'ui-resizable-handle ui-resizable-e',
15870 html : '  '
15876 var ctr = _this.el.select('.fc-event-container',true).first();
15877 var cg = ctr.createChild(cfg);
15879 var sbox = c.select('.fc-day-content',true).first().getBox();
15880 var ebox = c.select('.fc-day-content',true).first().getBox();
15882 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15883 cg.setWidth(ebox.right - sbox.x -2);
15885 cg.on('click', _this.onMoreEventClick, _this, c.more);
15895 onEventEnter: function (e, el,event,d) {
15896 this.fireEvent('evententer', this, el, event);
15899 onEventLeave: function (e, el,event,d) {
15900 this.fireEvent('eventleave', this, el, event);
15903 onEventClick: function (e, el,event,d) {
15904 this.fireEvent('eventclick', this, el, event);
15907 onMonthChange: function () {
15911 onMoreEventClick: function(e, el, more)
15915 this.calpopover.placement = 'right';
15916 this.calpopover.setTitle('More');
15918 this.calpopover.setContent('');
15920 var ctr = this.calpopover.el.select('.popover-content', true).first();
15922 Roo.each(more, function(m){
15924 cls : 'fc-event-hori fc-event-draggable',
15927 var cg = ctr.createChild(cfg);
15929 cg.on('click', _this.onEventClick, _this, m);
15932 this.calpopover.show(el);
15937 onLoad: function ()
15939 this.calevents = [];
15942 if(this.store.getCount() > 0){
15943 this.store.data.each(function(d){
15946 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15947 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15948 time : d.data.start_time,
15949 title : d.data.title,
15950 description : d.data.description,
15951 venue : d.data.venue
15956 this.renderEvents();
15958 if(this.calevents.length && this.loadMask){
15959 this.maskEl.hide();
15963 onBeforeLoad: function()
15965 this.clearEvents();
15967 this.maskEl.show();
15981 * @class Roo.bootstrap.Popover
15982 * @extends Roo.bootstrap.Component
15983 * Bootstrap Popover class
15984 * @cfg {String} html contents of the popover (or false to use children..)
15985 * @cfg {String} title of popover (or false to hide)
15986 * @cfg {String} placement how it is placed
15987 * @cfg {String} trigger click || hover (or false to trigger manually)
15988 * @cfg {String} over what (parent or false to trigger manually.)
15989 * @cfg {Number} delay - delay before showing
15992 * Create a new Popover
15993 * @param {Object} config The config object
15996 Roo.bootstrap.Popover = function(config){
15997 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16003 * After the popover show
16005 * @param {Roo.bootstrap.Popover} this
16010 * After the popover hide
16012 * @param {Roo.bootstrap.Popover} this
16018 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16020 title: 'Fill in a title',
16023 placement : 'right',
16024 trigger : 'hover', // hover
16030 can_build_overlaid : false,
16032 getChildContainer : function()
16034 return this.el.select('.popover-content',true).first();
16037 getAutoCreate : function(){
16040 cls : 'popover roo-dynamic',
16041 style: 'display:block',
16047 cls : 'popover-inner',
16051 cls: 'popover-title',
16055 cls : 'popover-content',
16066 setTitle: function(str)
16069 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16071 setContent: function(str)
16074 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16076 // as it get's added to the bottom of the page.
16077 onRender : function(ct, position)
16079 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16081 var cfg = Roo.apply({}, this.getAutoCreate());
16085 cfg.cls += ' ' + this.cls;
16088 cfg.style = this.style;
16090 //Roo.log("adding to ");
16091 this.el = Roo.get(document.body).createChild(cfg, position);
16092 // Roo.log(this.el);
16097 initEvents : function()
16099 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16100 this.el.enableDisplayMode('block');
16102 if (this.over === false) {
16105 if (this.triggers === false) {
16108 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16109 var triggers = this.trigger ? this.trigger.split(' ') : [];
16110 Roo.each(triggers, function(trigger) {
16112 if (trigger == 'click') {
16113 on_el.on('click', this.toggle, this);
16114 } else if (trigger != 'manual') {
16115 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16116 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16118 on_el.on(eventIn ,this.enter, this);
16119 on_el.on(eventOut, this.leave, this);
16130 toggle : function () {
16131 this.hoverState == 'in' ? this.leave() : this.enter();
16134 enter : function () {
16136 clearTimeout(this.timeout);
16138 this.hoverState = 'in';
16140 if (!this.delay || !this.delay.show) {
16145 this.timeout = setTimeout(function () {
16146 if (_t.hoverState == 'in') {
16149 }, this.delay.show)
16152 leave : function() {
16153 clearTimeout(this.timeout);
16155 this.hoverState = 'out';
16157 if (!this.delay || !this.delay.hide) {
16162 this.timeout = setTimeout(function () {
16163 if (_t.hoverState == 'out') {
16166 }, this.delay.hide)
16169 show : function (on_el)
16172 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16176 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16177 if (this.html !== false) {
16178 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16180 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16181 if (!this.title.length) {
16182 this.el.select('.popover-title',true).hide();
16185 var placement = typeof this.placement == 'function' ?
16186 this.placement.call(this, this.el, on_el) :
16189 var autoToken = /\s?auto?\s?/i;
16190 var autoPlace = autoToken.test(placement);
16192 placement = placement.replace(autoToken, '') || 'top';
16196 //this.el.setXY([0,0]);
16198 this.el.dom.style.display='block';
16199 this.el.addClass(placement);
16201 //this.el.appendTo(on_el);
16203 var p = this.getPosition();
16204 var box = this.el.getBox();
16209 var align = Roo.bootstrap.Popover.alignment[placement];
16210 this.el.alignTo(on_el, align[0],align[1]);
16211 //var arrow = this.el.select('.arrow',true).first();
16212 //arrow.set(align[2],
16214 this.el.addClass('in');
16217 if (this.el.hasClass('fade')) {
16221 this.hoverState = 'in';
16223 this.fireEvent('show', this);
16228 this.el.setXY([0,0]);
16229 this.el.removeClass('in');
16231 this.hoverState = null;
16233 this.fireEvent('hide', this);
16238 Roo.bootstrap.Popover.alignment = {
16239 'left' : ['r-l', [-10,0], 'right'],
16240 'right' : ['l-r', [10,0], 'left'],
16241 'bottom' : ['t-b', [0,10], 'top'],
16242 'top' : [ 'b-t', [0,-10], 'bottom']
16253 * @class Roo.bootstrap.Progress
16254 * @extends Roo.bootstrap.Component
16255 * Bootstrap Progress class
16256 * @cfg {Boolean} striped striped of the progress bar
16257 * @cfg {Boolean} active animated of the progress bar
16261 * Create a new Progress
16262 * @param {Object} config The config object
16265 Roo.bootstrap.Progress = function(config){
16266 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16269 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16274 getAutoCreate : function(){
16282 cfg.cls += ' progress-striped';
16286 cfg.cls += ' active';
16305 * @class Roo.bootstrap.ProgressBar
16306 * @extends Roo.bootstrap.Component
16307 * Bootstrap ProgressBar class
16308 * @cfg {Number} aria_valuenow aria-value now
16309 * @cfg {Number} aria_valuemin aria-value min
16310 * @cfg {Number} aria_valuemax aria-value max
16311 * @cfg {String} label label for the progress bar
16312 * @cfg {String} panel (success | info | warning | danger )
16313 * @cfg {String} role role of the progress bar
16314 * @cfg {String} sr_only text
16318 * Create a new ProgressBar
16319 * @param {Object} config The config object
16322 Roo.bootstrap.ProgressBar = function(config){
16323 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16326 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16330 aria_valuemax : 100,
16336 getAutoCreate : function()
16341 cls: 'progress-bar',
16342 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16354 cfg.role = this.role;
16357 if(this.aria_valuenow){
16358 cfg['aria-valuenow'] = this.aria_valuenow;
16361 if(this.aria_valuemin){
16362 cfg['aria-valuemin'] = this.aria_valuemin;
16365 if(this.aria_valuemax){
16366 cfg['aria-valuemax'] = this.aria_valuemax;
16369 if(this.label && !this.sr_only){
16370 cfg.html = this.label;
16374 cfg.cls += ' progress-bar-' + this.panel;
16380 update : function(aria_valuenow)
16382 this.aria_valuenow = aria_valuenow;
16384 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16399 * @class Roo.bootstrap.TabGroup
16400 * @extends Roo.bootstrap.Column
16401 * Bootstrap Column class
16402 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16403 * @cfg {Boolean} carousel true to make the group behave like a carousel
16404 * @cfg {Boolean} bullets show bullets for the panels
16405 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16406 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16407 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16410 * Create a new TabGroup
16411 * @param {Object} config The config object
16414 Roo.bootstrap.TabGroup = function(config){
16415 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16417 this.navId = Roo.id();
16420 Roo.bootstrap.TabGroup.register(this);
16424 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16427 transition : false,
16432 slideOnTouch : false,
16434 getAutoCreate : function()
16436 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16438 cfg.cls += ' tab-content';
16440 if (this.carousel) {
16441 cfg.cls += ' carousel slide';
16444 cls : 'carousel-inner'
16447 if(this.bullets && !Roo.isTouch){
16450 cls : 'carousel-bullets',
16454 if(this.bullets_cls){
16455 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16458 for (var i = 0; i < this.bullets; i++){
16460 cls : 'bullet bullet-' + i
16468 cfg.cn[0].cn = bullets;
16475 initEvents: function()
16477 if(Roo.isTouch && this.slideOnTouch){
16478 this.el.on("touchstart", this.onTouchStart, this);
16481 if(this.autoslide){
16484 this.slideFn = window.setInterval(function() {
16485 _this.showPanelNext();
16491 onTouchStart : function(e, el, o)
16493 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16497 this.showPanelNext();
16500 getChildContainer : function()
16502 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16506 * register a Navigation item
16507 * @param {Roo.bootstrap.NavItem} the navitem to add
16509 register : function(item)
16511 this.tabs.push( item);
16512 item.navId = this.navId; // not really needed..
16517 getActivePanel : function()
16520 Roo.each(this.tabs, function(t) {
16530 getPanelByName : function(n)
16533 Roo.each(this.tabs, function(t) {
16534 if (t.tabId == n) {
16542 indexOfPanel : function(p)
16545 Roo.each(this.tabs, function(t,i) {
16546 if (t.tabId == p.tabId) {
16555 * show a specific panel
16556 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16557 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16559 showPanel : function (pan)
16561 if(this.transition || typeof(pan) == 'undefined'){
16562 Roo.log("waiting for the transitionend");
16566 if (typeof(pan) == 'number') {
16567 pan = this.tabs[pan];
16570 if (typeof(pan) == 'string') {
16571 pan = this.getPanelByName(pan);
16574 var cur = this.getActivePanel();
16577 Roo.log('pan or acitve pan is undefined');
16581 if (pan.tabId == this.getActivePanel().tabId) {
16585 if (false === cur.fireEvent('beforedeactivate')) {
16589 if(this.bullets > 0 && !Roo.isTouch){
16590 this.setActiveBullet(this.indexOfPanel(pan));
16593 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16595 this.transition = true;
16596 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16597 var lr = dir == 'next' ? 'left' : 'right';
16598 pan.el.addClass(dir); // or prev
16599 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16600 cur.el.addClass(lr); // or right
16601 pan.el.addClass(lr);
16604 cur.el.on('transitionend', function() {
16605 Roo.log("trans end?");
16607 pan.el.removeClass([lr,dir]);
16608 pan.setActive(true);
16610 cur.el.removeClass([lr]);
16611 cur.setActive(false);
16613 _this.transition = false;
16615 }, this, { single: true } );
16620 cur.setActive(false);
16621 pan.setActive(true);
16626 showPanelNext : function()
16628 var i = this.indexOfPanel(this.getActivePanel());
16630 if (i >= this.tabs.length - 1 && !this.autoslide) {
16634 if (i >= this.tabs.length - 1 && this.autoslide) {
16638 this.showPanel(this.tabs[i+1]);
16641 showPanelPrev : function()
16643 var i = this.indexOfPanel(this.getActivePanel());
16645 if (i < 1 && !this.autoslide) {
16649 if (i < 1 && this.autoslide) {
16650 i = this.tabs.length;
16653 this.showPanel(this.tabs[i-1]);
16657 addBullet: function()
16659 if(!this.bullets || Roo.isTouch){
16662 var ctr = this.el.select('.carousel-bullets',true).first();
16663 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16664 var bullet = ctr.createChild({
16665 cls : 'bullet bullet-' + i
16666 },ctr.dom.lastChild);
16671 bullet.on('click', (function(e, el, o, ii, t){
16673 e.preventDefault();
16675 this.showPanel(ii);
16677 if(this.autoslide && this.slideFn){
16678 clearInterval(this.slideFn);
16679 this.slideFn = window.setInterval(function() {
16680 _this.showPanelNext();
16684 }).createDelegate(this, [i, bullet], true));
16689 setActiveBullet : function(i)
16695 Roo.each(this.el.select('.bullet', true).elements, function(el){
16696 el.removeClass('selected');
16699 var bullet = this.el.select('.bullet-' + i, true).first();
16705 bullet.addClass('selected');
16716 Roo.apply(Roo.bootstrap.TabGroup, {
16720 * register a Navigation Group
16721 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16723 register : function(navgrp)
16725 this.groups[navgrp.navId] = navgrp;
16729 * fetch a Navigation Group based on the navigation ID
16730 * if one does not exist , it will get created.
16731 * @param {string} the navgroup to add
16732 * @returns {Roo.bootstrap.NavGroup} the navgroup
16734 get: function(navId) {
16735 if (typeof(this.groups[navId]) == 'undefined') {
16736 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16738 return this.groups[navId] ;
16753 * @class Roo.bootstrap.TabPanel
16754 * @extends Roo.bootstrap.Component
16755 * Bootstrap TabPanel class
16756 * @cfg {Boolean} active panel active
16757 * @cfg {String} html panel content
16758 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16759 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16763 * Create a new TabPanel
16764 * @param {Object} config The config object
16767 Roo.bootstrap.TabPanel = function(config){
16768 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16772 * Fires when the active status changes
16773 * @param {Roo.bootstrap.TabPanel} this
16774 * @param {Boolean} state the new state
16779 * @event beforedeactivate
16780 * Fires before a tab is de-activated - can be used to do validation on a form.
16781 * @param {Roo.bootstrap.TabPanel} this
16782 * @return {Boolean} false if there is an error
16785 'beforedeactivate': true
16788 this.tabId = this.tabId || Roo.id();
16792 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16799 getAutoCreate : function(){
16802 // item is needed for carousel - not sure if it has any effect otherwise
16803 cls: 'tab-pane item',
16804 html: this.html || ''
16808 cfg.cls += ' active';
16812 cfg.tabId = this.tabId;
16819 initEvents: function()
16821 var p = this.parent();
16822 this.navId = this.navId || p.navId;
16824 if (typeof(this.navId) != 'undefined') {
16825 // not really needed.. but just in case.. parent should be a NavGroup.
16826 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16830 var i = tg.tabs.length - 1;
16832 if(this.active && tg.bullets > 0 && i < tg.bullets){
16833 tg.setActiveBullet(i);
16840 onRender : function(ct, position)
16842 // Roo.log("Call onRender: " + this.xtype);
16844 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16852 setActive: function(state)
16854 Roo.log("panel - set active " + this.tabId + "=" + state);
16856 this.active = state;
16858 this.el.removeClass('active');
16860 } else if (!this.el.hasClass('active')) {
16861 this.el.addClass('active');
16864 this.fireEvent('changed', this, state);
16881 * @class Roo.bootstrap.DateField
16882 * @extends Roo.bootstrap.Input
16883 * Bootstrap DateField class
16884 * @cfg {Number} weekStart default 0
16885 * @cfg {String} viewMode default empty, (months|years)
16886 * @cfg {String} minViewMode default empty, (months|years)
16887 * @cfg {Number} startDate default -Infinity
16888 * @cfg {Number} endDate default Infinity
16889 * @cfg {Boolean} todayHighlight default false
16890 * @cfg {Boolean} todayBtn default false
16891 * @cfg {Boolean} calendarWeeks default false
16892 * @cfg {Object} daysOfWeekDisabled default empty
16893 * @cfg {Boolean} singleMode default false (true | false)
16895 * @cfg {Boolean} keyboardNavigation default true
16896 * @cfg {String} language default en
16899 * Create a new DateField
16900 * @param {Object} config The config object
16903 Roo.bootstrap.DateField = function(config){
16904 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16908 * Fires when this field show.
16909 * @param {Roo.bootstrap.DateField} this
16910 * @param {Mixed} date The date value
16915 * Fires when this field hide.
16916 * @param {Roo.bootstrap.DateField} this
16917 * @param {Mixed} date The date value
16922 * Fires when select a date.
16923 * @param {Roo.bootstrap.DateField} this
16924 * @param {Mixed} date The date value
16928 * @event beforeselect
16929 * Fires when before select a date.
16930 * @param {Roo.bootstrap.DateField} this
16931 * @param {Mixed} date The date value
16933 beforeselect : true
16937 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16940 * @cfg {String} format
16941 * The default date format string which can be overriden for localization support. The format must be
16942 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16946 * @cfg {String} altFormats
16947 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16948 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16950 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16958 todayHighlight : false,
16964 keyboardNavigation: true,
16966 calendarWeeks: false,
16968 startDate: -Infinity,
16972 daysOfWeekDisabled: [],
16976 singleMode : false,
16978 UTCDate: function()
16980 return new Date(Date.UTC.apply(Date, arguments));
16983 UTCToday: function()
16985 var today = new Date();
16986 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16989 getDate: function() {
16990 var d = this.getUTCDate();
16991 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16994 getUTCDate: function() {
16998 setDate: function(d) {
16999 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17002 setUTCDate: function(d) {
17004 this.setValue(this.formatDate(this.date));
17007 onRender: function(ct, position)
17010 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17012 this.language = this.language || 'en';
17013 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17014 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17016 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17017 this.format = this.format || 'm/d/y';
17018 this.isInline = false;
17019 this.isInput = true;
17020 this.component = this.el.select('.add-on', true).first() || false;
17021 this.component = (this.component && this.component.length === 0) ? false : this.component;
17022 this.hasInput = this.component && this.inputEL().length;
17024 if (typeof(this.minViewMode === 'string')) {
17025 switch (this.minViewMode) {
17027 this.minViewMode = 1;
17030 this.minViewMode = 2;
17033 this.minViewMode = 0;
17038 if (typeof(this.viewMode === 'string')) {
17039 switch (this.viewMode) {
17052 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17054 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17056 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17058 this.picker().on('mousedown', this.onMousedown, this);
17059 this.picker().on('click', this.onClick, this);
17061 this.picker().addClass('datepicker-dropdown');
17063 this.startViewMode = this.viewMode;
17065 if(this.singleMode){
17066 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17067 v.setVisibilityMode(Roo.Element.DISPLAY);
17071 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17072 v.setStyle('width', '189px');
17076 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17077 if(!this.calendarWeeks){
17082 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17083 v.attr('colspan', function(i, val){
17084 return parseInt(val) + 1;
17089 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17091 this.setStartDate(this.startDate);
17092 this.setEndDate(this.endDate);
17094 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17101 if(this.isInline) {
17106 picker : function()
17108 return this.pickerEl;
17109 // return this.el.select('.datepicker', true).first();
17112 fillDow: function()
17114 var dowCnt = this.weekStart;
17123 if(this.calendarWeeks){
17131 while (dowCnt < this.weekStart + 7) {
17135 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17139 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17142 fillMonths: function()
17145 var months = this.picker().select('>.datepicker-months td', true).first();
17147 months.dom.innerHTML = '';
17153 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17156 months.createChild(month);
17163 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;
17165 if (this.date < this.startDate) {
17166 this.viewDate = new Date(this.startDate);
17167 } else if (this.date > this.endDate) {
17168 this.viewDate = new Date(this.endDate);
17170 this.viewDate = new Date(this.date);
17178 var d = new Date(this.viewDate),
17179 year = d.getUTCFullYear(),
17180 month = d.getUTCMonth(),
17181 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17182 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17183 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17184 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17185 currentDate = this.date && this.date.valueOf(),
17186 today = this.UTCToday();
17188 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17190 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17192 // this.picker.select('>tfoot th.today').
17193 // .text(dates[this.language].today)
17194 // .toggle(this.todayBtn !== false);
17196 this.updateNavArrows();
17199 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17201 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17203 prevMonth.setUTCDate(day);
17205 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17207 var nextMonth = new Date(prevMonth);
17209 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17211 nextMonth = nextMonth.valueOf();
17213 var fillMonths = false;
17215 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17217 while(prevMonth.valueOf() < nextMonth) {
17220 if (prevMonth.getUTCDay() === this.weekStart) {
17222 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17230 if(this.calendarWeeks){
17231 // ISO 8601: First week contains first thursday.
17232 // ISO also states week starts on Monday, but we can be more abstract here.
17234 // Start of current week: based on weekstart/current date
17235 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17236 // Thursday of this week
17237 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17238 // First Thursday of year, year from thursday
17239 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17240 // Calendar week: ms between thursdays, div ms per day, div 7 days
17241 calWeek = (th - yth) / 864e5 / 7 + 1;
17243 fillMonths.cn.push({
17251 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17253 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17256 if (this.todayHighlight &&
17257 prevMonth.getUTCFullYear() == today.getFullYear() &&
17258 prevMonth.getUTCMonth() == today.getMonth() &&
17259 prevMonth.getUTCDate() == today.getDate()) {
17260 clsName += ' today';
17263 if (currentDate && prevMonth.valueOf() === currentDate) {
17264 clsName += ' active';
17267 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17268 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17269 clsName += ' disabled';
17272 fillMonths.cn.push({
17274 cls: 'day ' + clsName,
17275 html: prevMonth.getDate()
17278 prevMonth.setDate(prevMonth.getDate()+1);
17281 var currentYear = this.date && this.date.getUTCFullYear();
17282 var currentMonth = this.date && this.date.getUTCMonth();
17284 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17286 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17287 v.removeClass('active');
17289 if(currentYear === year && k === currentMonth){
17290 v.addClass('active');
17293 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17294 v.addClass('disabled');
17300 year = parseInt(year/10, 10) * 10;
17302 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17304 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17307 for (var i = -1; i < 11; i++) {
17308 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17310 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17318 showMode: function(dir)
17321 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17324 Roo.each(this.picker().select('>div',true).elements, function(v){
17325 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17328 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17333 if(this.isInline) {
17337 this.picker().removeClass(['bottom', 'top']);
17339 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17341 * place to the top of element!
17345 this.picker().addClass('top');
17346 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17351 this.picker().addClass('bottom');
17353 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17356 parseDate : function(value)
17358 if(!value || value instanceof Date){
17361 var v = Date.parseDate(value, this.format);
17362 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17363 v = Date.parseDate(value, 'Y-m-d');
17365 if(!v && this.altFormats){
17366 if(!this.altFormatsArray){
17367 this.altFormatsArray = this.altFormats.split("|");
17369 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17370 v = Date.parseDate(value, this.altFormatsArray[i]);
17376 formatDate : function(date, fmt)
17378 return (!date || !(date instanceof Date)) ?
17379 date : date.dateFormat(fmt || this.format);
17382 onFocus : function()
17384 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17388 onBlur : function()
17390 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17392 var d = this.inputEl().getValue();
17401 this.picker().show();
17405 this.fireEvent('show', this, this.date);
17410 if(this.isInline) {
17413 this.picker().hide();
17414 this.viewMode = this.startViewMode;
17417 this.fireEvent('hide', this, this.date);
17421 onMousedown: function(e)
17423 e.stopPropagation();
17424 e.preventDefault();
17429 Roo.bootstrap.DateField.superclass.keyup.call(this);
17433 setValue: function(v)
17435 if(this.fireEvent('beforeselect', this, v) !== false){
17436 var d = new Date(this.parseDate(v) ).clearTime();
17438 if(isNaN(d.getTime())){
17439 this.date = this.viewDate = '';
17440 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17444 v = this.formatDate(d);
17446 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17448 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17452 this.fireEvent('select', this, this.date);
17456 getValue: function()
17458 return this.formatDate(this.date);
17461 fireKey: function(e)
17463 if (!this.picker().isVisible()){
17464 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17470 var dateChanged = false,
17472 newDate, newViewDate;
17477 e.preventDefault();
17481 if (!this.keyboardNavigation) {
17484 dir = e.keyCode == 37 ? -1 : 1;
17487 newDate = this.moveYear(this.date, dir);
17488 newViewDate = this.moveYear(this.viewDate, dir);
17489 } else if (e.shiftKey){
17490 newDate = this.moveMonth(this.date, dir);
17491 newViewDate = this.moveMonth(this.viewDate, dir);
17493 newDate = new Date(this.date);
17494 newDate.setUTCDate(this.date.getUTCDate() + dir);
17495 newViewDate = new Date(this.viewDate);
17496 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17498 if (this.dateWithinRange(newDate)){
17499 this.date = newDate;
17500 this.viewDate = newViewDate;
17501 this.setValue(this.formatDate(this.date));
17503 e.preventDefault();
17504 dateChanged = true;
17509 if (!this.keyboardNavigation) {
17512 dir = e.keyCode == 38 ? -1 : 1;
17514 newDate = this.moveYear(this.date, dir);
17515 newViewDate = this.moveYear(this.viewDate, dir);
17516 } else if (e.shiftKey){
17517 newDate = this.moveMonth(this.date, dir);
17518 newViewDate = this.moveMonth(this.viewDate, dir);
17520 newDate = new Date(this.date);
17521 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17522 newViewDate = new Date(this.viewDate);
17523 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17525 if (this.dateWithinRange(newDate)){
17526 this.date = newDate;
17527 this.viewDate = newViewDate;
17528 this.setValue(this.formatDate(this.date));
17530 e.preventDefault();
17531 dateChanged = true;
17535 this.setValue(this.formatDate(this.date));
17537 e.preventDefault();
17540 this.setValue(this.formatDate(this.date));
17554 onClick: function(e)
17556 e.stopPropagation();
17557 e.preventDefault();
17559 var target = e.getTarget();
17561 if(target.nodeName.toLowerCase() === 'i'){
17562 target = Roo.get(target).dom.parentNode;
17565 var nodeName = target.nodeName;
17566 var className = target.className;
17567 var html = target.innerHTML;
17568 //Roo.log(nodeName);
17570 switch(nodeName.toLowerCase()) {
17572 switch(className) {
17578 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17579 switch(this.viewMode){
17581 this.viewDate = this.moveMonth(this.viewDate, dir);
17585 this.viewDate = this.moveYear(this.viewDate, dir);
17591 var date = new Date();
17592 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17594 this.setValue(this.formatDate(this.date));
17601 if (className.indexOf('disabled') < 0) {
17602 this.viewDate.setUTCDate(1);
17603 if (className.indexOf('month') > -1) {
17604 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17606 var year = parseInt(html, 10) || 0;
17607 this.viewDate.setUTCFullYear(year);
17611 if(this.singleMode){
17612 this.setValue(this.formatDate(this.viewDate));
17623 //Roo.log(className);
17624 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17625 var day = parseInt(html, 10) || 1;
17626 var year = this.viewDate.getUTCFullYear(),
17627 month = this.viewDate.getUTCMonth();
17629 if (className.indexOf('old') > -1) {
17636 } else if (className.indexOf('new') > -1) {
17644 //Roo.log([year,month,day]);
17645 this.date = this.UTCDate(year, month, day,0,0,0,0);
17646 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17648 //Roo.log(this.formatDate(this.date));
17649 this.setValue(this.formatDate(this.date));
17656 setStartDate: function(startDate)
17658 this.startDate = startDate || -Infinity;
17659 if (this.startDate !== -Infinity) {
17660 this.startDate = this.parseDate(this.startDate);
17663 this.updateNavArrows();
17666 setEndDate: function(endDate)
17668 this.endDate = endDate || Infinity;
17669 if (this.endDate !== Infinity) {
17670 this.endDate = this.parseDate(this.endDate);
17673 this.updateNavArrows();
17676 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17678 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17679 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17680 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17682 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17683 return parseInt(d, 10);
17686 this.updateNavArrows();
17689 updateNavArrows: function()
17691 if(this.singleMode){
17695 var d = new Date(this.viewDate),
17696 year = d.getUTCFullYear(),
17697 month = d.getUTCMonth();
17699 Roo.each(this.picker().select('.prev', true).elements, function(v){
17701 switch (this.viewMode) {
17704 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17710 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17717 Roo.each(this.picker().select('.next', true).elements, function(v){
17719 switch (this.viewMode) {
17722 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17728 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17736 moveMonth: function(date, dir)
17741 var new_date = new Date(date.valueOf()),
17742 day = new_date.getUTCDate(),
17743 month = new_date.getUTCMonth(),
17744 mag = Math.abs(dir),
17746 dir = dir > 0 ? 1 : -1;
17749 // If going back one month, make sure month is not current month
17750 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17752 return new_date.getUTCMonth() == month;
17754 // If going forward one month, make sure month is as expected
17755 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17757 return new_date.getUTCMonth() != new_month;
17759 new_month = month + dir;
17760 new_date.setUTCMonth(new_month);
17761 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17762 if (new_month < 0 || new_month > 11) {
17763 new_month = (new_month + 12) % 12;
17766 // For magnitudes >1, move one month at a time...
17767 for (var i=0; i<mag; i++) {
17768 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17769 new_date = this.moveMonth(new_date, dir);
17771 // ...then reset the day, keeping it in the new month
17772 new_month = new_date.getUTCMonth();
17773 new_date.setUTCDate(day);
17775 return new_month != new_date.getUTCMonth();
17778 // Common date-resetting loop -- if date is beyond end of month, make it
17781 new_date.setUTCDate(--day);
17782 new_date.setUTCMonth(new_month);
17787 moveYear: function(date, dir)
17789 return this.moveMonth(date, dir*12);
17792 dateWithinRange: function(date)
17794 return date >= this.startDate && date <= this.endDate;
17800 this.picker().remove();
17805 Roo.apply(Roo.bootstrap.DateField, {
17816 html: '<i class="fa fa-arrow-left"/>'
17826 html: '<i class="fa fa-arrow-right"/>'
17868 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17869 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17870 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17871 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17872 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17885 navFnc: 'FullYear',
17890 navFnc: 'FullYear',
17895 Roo.apply(Roo.bootstrap.DateField, {
17899 cls: 'datepicker dropdown-menu roo-dynamic',
17903 cls: 'datepicker-days',
17907 cls: 'table-condensed',
17909 Roo.bootstrap.DateField.head,
17913 Roo.bootstrap.DateField.footer
17920 cls: 'datepicker-months',
17924 cls: 'table-condensed',
17926 Roo.bootstrap.DateField.head,
17927 Roo.bootstrap.DateField.content,
17928 Roo.bootstrap.DateField.footer
17935 cls: 'datepicker-years',
17939 cls: 'table-condensed',
17941 Roo.bootstrap.DateField.head,
17942 Roo.bootstrap.DateField.content,
17943 Roo.bootstrap.DateField.footer
17962 * @class Roo.bootstrap.TimeField
17963 * @extends Roo.bootstrap.Input
17964 * Bootstrap DateField class
17968 * Create a new TimeField
17969 * @param {Object} config The config object
17972 Roo.bootstrap.TimeField = function(config){
17973 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17977 * Fires when this field show.
17978 * @param {Roo.bootstrap.DateField} thisthis
17979 * @param {Mixed} date The date value
17984 * Fires when this field hide.
17985 * @param {Roo.bootstrap.DateField} this
17986 * @param {Mixed} date The date value
17991 * Fires when select a date.
17992 * @param {Roo.bootstrap.DateField} this
17993 * @param {Mixed} date The date value
17999 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18002 * @cfg {String} format
18003 * The default time format string which can be overriden for localization support. The format must be
18004 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18008 onRender: function(ct, position)
18011 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18013 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18015 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18017 this.pop = this.picker().select('>.datepicker-time',true).first();
18018 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18020 this.picker().on('mousedown', this.onMousedown, this);
18021 this.picker().on('click', this.onClick, this);
18023 this.picker().addClass('datepicker-dropdown');
18028 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18029 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18030 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18031 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18032 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18033 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18037 fireKey: function(e){
18038 if (!this.picker().isVisible()){
18039 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18045 e.preventDefault();
18053 this.onTogglePeriod();
18056 this.onIncrementMinutes();
18059 this.onDecrementMinutes();
18068 onClick: function(e) {
18069 e.stopPropagation();
18070 e.preventDefault();
18073 picker : function()
18075 return this.el.select('.datepicker', true).first();
18078 fillTime: function()
18080 var time = this.pop.select('tbody', true).first();
18082 time.dom.innerHTML = '';
18097 cls: 'hours-up glyphicon glyphicon-chevron-up'
18117 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18138 cls: 'timepicker-hour',
18153 cls: 'timepicker-minute',
18168 cls: 'btn btn-primary period',
18190 cls: 'hours-down glyphicon glyphicon-chevron-down'
18210 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18228 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18235 var hours = this.time.getHours();
18236 var minutes = this.time.getMinutes();
18249 hours = hours - 12;
18253 hours = '0' + hours;
18257 minutes = '0' + minutes;
18260 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18261 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18262 this.pop.select('button', true).first().dom.innerHTML = period;
18268 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18270 var cls = ['bottom'];
18272 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18279 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18284 this.picker().addClass(cls.join('-'));
18288 Roo.each(cls, function(c){
18290 _this.picker().setTop(_this.inputEl().getHeight());
18294 _this.picker().setTop(0 - _this.picker().getHeight());
18299 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18303 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18310 onFocus : function()
18312 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18316 onBlur : function()
18318 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18324 this.picker().show();
18329 this.fireEvent('show', this, this.date);
18334 this.picker().hide();
18337 this.fireEvent('hide', this, this.date);
18340 setTime : function()
18343 this.setValue(this.time.format(this.format));
18345 this.fireEvent('select', this, this.date);
18350 onMousedown: function(e){
18351 e.stopPropagation();
18352 e.preventDefault();
18355 onIncrementHours: function()
18357 Roo.log('onIncrementHours');
18358 this.time = this.time.add(Date.HOUR, 1);
18363 onDecrementHours: function()
18365 Roo.log('onDecrementHours');
18366 this.time = this.time.add(Date.HOUR, -1);
18370 onIncrementMinutes: function()
18372 Roo.log('onIncrementMinutes');
18373 this.time = this.time.add(Date.MINUTE, 1);
18377 onDecrementMinutes: function()
18379 Roo.log('onDecrementMinutes');
18380 this.time = this.time.add(Date.MINUTE, -1);
18384 onTogglePeriod: function()
18386 Roo.log('onTogglePeriod');
18387 this.time = this.time.add(Date.HOUR, 12);
18394 Roo.apply(Roo.bootstrap.TimeField, {
18424 cls: 'btn btn-info ok',
18436 Roo.apply(Roo.bootstrap.TimeField, {
18440 cls: 'datepicker dropdown-menu',
18444 cls: 'datepicker-time',
18448 cls: 'table-condensed',
18450 Roo.bootstrap.TimeField.content,
18451 Roo.bootstrap.TimeField.footer
18470 * @class Roo.bootstrap.MonthField
18471 * @extends Roo.bootstrap.Input
18472 * Bootstrap MonthField class
18474 * @cfg {String} language default en
18477 * Create a new MonthField
18478 * @param {Object} config The config object
18481 Roo.bootstrap.MonthField = function(config){
18482 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18487 * Fires when this field show.
18488 * @param {Roo.bootstrap.MonthField} this
18489 * @param {Mixed} date The date value
18494 * Fires when this field hide.
18495 * @param {Roo.bootstrap.MonthField} this
18496 * @param {Mixed} date The date value
18501 * Fires when select a date.
18502 * @param {Roo.bootstrap.MonthField} this
18503 * @param {String} oldvalue The old value
18504 * @param {String} newvalue The new value
18510 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18512 onRender: function(ct, position)
18515 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18517 this.language = this.language || 'en';
18518 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18519 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18521 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18522 this.isInline = false;
18523 this.isInput = true;
18524 this.component = this.el.select('.add-on', true).first() || false;
18525 this.component = (this.component && this.component.length === 0) ? false : this.component;
18526 this.hasInput = this.component && this.inputEL().length;
18528 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18530 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18532 this.picker().on('mousedown', this.onMousedown, this);
18533 this.picker().on('click', this.onClick, this);
18535 this.picker().addClass('datepicker-dropdown');
18537 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18538 v.setStyle('width', '189px');
18545 if(this.isInline) {
18551 setValue: function(v, suppressEvent)
18553 var o = this.getValue();
18555 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18559 if(suppressEvent !== true){
18560 this.fireEvent('select', this, o, v);
18565 getValue: function()
18570 onClick: function(e)
18572 e.stopPropagation();
18573 e.preventDefault();
18575 var target = e.getTarget();
18577 if(target.nodeName.toLowerCase() === 'i'){
18578 target = Roo.get(target).dom.parentNode;
18581 var nodeName = target.nodeName;
18582 var className = target.className;
18583 var html = target.innerHTML;
18585 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18589 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18591 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18597 picker : function()
18599 return this.pickerEl;
18602 fillMonths: function()
18605 var months = this.picker().select('>.datepicker-months td', true).first();
18607 months.dom.innerHTML = '';
18613 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18616 months.createChild(month);
18625 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18626 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18629 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18630 e.removeClass('active');
18632 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18633 e.addClass('active');
18640 if(this.isInline) {
18644 this.picker().removeClass(['bottom', 'top']);
18646 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18648 * place to the top of element!
18652 this.picker().addClass('top');
18653 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18658 this.picker().addClass('bottom');
18660 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18663 onFocus : function()
18665 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18669 onBlur : function()
18671 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18673 var d = this.inputEl().getValue();
18682 this.picker().show();
18683 this.picker().select('>.datepicker-months', true).first().show();
18687 this.fireEvent('show', this, this.date);
18692 if(this.isInline) {
18695 this.picker().hide();
18696 this.fireEvent('hide', this, this.date);
18700 onMousedown: function(e)
18702 e.stopPropagation();
18703 e.preventDefault();
18708 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18712 fireKey: function(e)
18714 if (!this.picker().isVisible()){
18715 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18726 e.preventDefault();
18730 dir = e.keyCode == 37 ? -1 : 1;
18732 this.vIndex = this.vIndex + dir;
18734 if(this.vIndex < 0){
18738 if(this.vIndex > 11){
18742 if(isNaN(this.vIndex)){
18746 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18752 dir = e.keyCode == 38 ? -1 : 1;
18754 this.vIndex = this.vIndex + dir * 4;
18756 if(this.vIndex < 0){
18760 if(this.vIndex > 11){
18764 if(isNaN(this.vIndex)){
18768 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18773 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18774 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18778 e.preventDefault();
18781 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18782 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18798 this.picker().remove();
18803 Roo.apply(Roo.bootstrap.MonthField, {
18822 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18823 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18828 Roo.apply(Roo.bootstrap.MonthField, {
18832 cls: 'datepicker dropdown-menu roo-dynamic',
18836 cls: 'datepicker-months',
18840 cls: 'table-condensed',
18842 Roo.bootstrap.DateField.content
18862 * @class Roo.bootstrap.CheckBox
18863 * @extends Roo.bootstrap.Input
18864 * Bootstrap CheckBox class
18866 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18867 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18868 * @cfg {String} boxLabel The text that appears beside the checkbox
18869 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18870 * @cfg {Boolean} checked initnal the element
18871 * @cfg {Boolean} inline inline the element (default false)
18872 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18875 * Create a new CheckBox
18876 * @param {Object} config The config object
18879 Roo.bootstrap.CheckBox = function(config){
18880 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18885 * Fires when the element is checked or unchecked.
18886 * @param {Roo.bootstrap.CheckBox} this This input
18887 * @param {Boolean} checked The new checked value
18894 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18896 inputType: 'checkbox',
18904 getAutoCreate : function()
18906 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18912 cfg.cls = 'form-group ' + this.inputType; //input-group
18915 cfg.cls += ' ' + this.inputType + '-inline';
18921 type : this.inputType,
18922 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18923 cls : 'roo-' + this.inputType, //'form-box',
18924 placeholder : this.placeholder || ''
18928 if (this.weight) { // Validity check?
18929 cfg.cls += " " + this.inputType + "-" + this.weight;
18932 if (this.disabled) {
18933 input.disabled=true;
18937 input.checked = this.checked;
18941 input.name = this.name;
18945 input.cls += ' input-' + this.size;
18950 ['xs','sm','md','lg'].map(function(size){
18951 if (settings[size]) {
18952 cfg.cls += ' col-' + size + '-' + settings[size];
18956 var inputblock = input;
18958 if (this.before || this.after) {
18961 cls : 'input-group',
18966 inputblock.cn.push({
18968 cls : 'input-group-addon',
18973 inputblock.cn.push(input);
18976 inputblock.cn.push({
18978 cls : 'input-group-addon',
18985 if (align ==='left' && this.fieldLabel.length) {
18986 // Roo.log("left and has label");
18992 cls : 'control-label col-md-' + this.labelWidth,
18993 html : this.fieldLabel
18997 cls : "col-md-" + (12 - this.labelWidth),
19004 } else if ( this.fieldLabel.length) {
19005 // Roo.log(" label");
19009 tag: this.boxLabel ? 'span' : 'label',
19011 cls: 'control-label box-input-label',
19012 //cls : 'input-group-addon',
19013 html : this.fieldLabel
19023 // Roo.log(" no label && no align");
19024 cfg.cn = [ inputblock ] ;
19030 var boxLabelCfg = {
19032 //'for': id, // box label is handled by onclick - so no for...
19034 html: this.boxLabel
19038 boxLabelCfg.tooltip = this.tooltip;
19041 cfg.cn.push(boxLabelCfg);
19051 * return the real input element.
19053 inputEl: function ()
19055 return this.el.select('input.roo-' + this.inputType,true).first();
19058 labelEl: function()
19060 return this.el.select('label.control-label',true).first();
19062 /* depricated... */
19066 return this.labelEl();
19069 boxLabelEl: function()
19071 return this.el.select('label.box-label',true).first();
19074 initEvents : function()
19076 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19078 this.inputEl().on('click', this.onClick, this);
19080 if (this.boxLabel) {
19081 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19084 this.startValue = this.getValue();
19087 Roo.bootstrap.CheckBox.register(this);
19091 onClick : function()
19093 this.setChecked(!this.checked);
19096 setChecked : function(state,suppressEvent)
19098 this.startValue = this.getValue();
19100 if(this.inputType == 'radio'){
19102 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19103 e.dom.checked = false;
19106 this.inputEl().dom.checked = true;
19108 this.inputEl().dom.value = this.inputValue;
19110 if(suppressEvent !== true){
19111 this.fireEvent('check', this, true);
19119 this.checked = state;
19121 this.inputEl().dom.checked = state;
19123 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19125 if(suppressEvent !== true){
19126 this.fireEvent('check', this, state);
19132 getValue : function()
19134 if(this.inputType == 'radio'){
19135 return this.getGroupValue();
19138 return this.inputEl().getValue();
19142 getGroupValue : function()
19144 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19148 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19151 setValue : function(v,suppressEvent)
19153 if(this.inputType == 'radio'){
19154 this.setGroupValue(v, suppressEvent);
19158 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19163 setGroupValue : function(v, suppressEvent)
19165 this.startValue = this.getValue();
19167 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19168 e.dom.checked = false;
19170 if(e.dom.value == v){
19171 e.dom.checked = true;
19175 if(suppressEvent !== true){
19176 this.fireEvent('check', this, true);
19184 validate : function()
19188 (this.inputType == 'radio' && this.validateRadio()) ||
19189 (this.inputType == 'checkbox' && this.validateCheckbox())
19195 this.markInvalid();
19199 validateRadio : function()
19203 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19204 if(!e.dom.checked){
19216 validateCheckbox : function()
19219 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19222 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19230 for(var i in group){
19235 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19242 * Mark this field as valid
19244 markValid : function()
19246 if(this.allowBlank){
19252 this.fireEvent('valid', this);
19254 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19257 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19264 if(this.inputType == 'radio'){
19265 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19266 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19267 e.findParent('.form-group', false, true).addClass(_this.validClass);
19274 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19275 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19279 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19285 for(var i in group){
19286 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19287 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19292 * Mark this field as invalid
19293 * @param {String} msg The validation message
19295 markInvalid : function(msg)
19297 if(this.allowBlank){
19303 this.fireEvent('invalid', this, msg);
19305 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19308 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19312 label.markInvalid();
19315 if(this.inputType == 'radio'){
19316 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19317 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19318 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19325 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19326 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19330 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19336 for(var i in group){
19337 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19338 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19345 Roo.apply(Roo.bootstrap.CheckBox, {
19350 * register a CheckBox Group
19351 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19353 register : function(checkbox)
19355 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19356 this.groups[checkbox.groupId] = {};
19359 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19363 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19367 * fetch a CheckBox Group based on the group ID
19368 * @param {string} the group ID
19369 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19371 get: function(groupId) {
19372 if (typeof(this.groups[groupId]) == 'undefined') {
19376 return this.groups[groupId] ;
19388 *<div class="radio">
19390 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19391 Option one is this and that—be sure to include why it's great
19398 *<label class="radio-inline">fieldLabel</label>
19399 *<label class="radio-inline">
19400 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19408 * @class Roo.bootstrap.Radio
19409 * @extends Roo.bootstrap.CheckBox
19410 * Bootstrap Radio class
19413 * Create a new Radio
19414 * @param {Object} config The config object
19417 Roo.bootstrap.Radio = function(config){
19418 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19422 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19424 inputType: 'radio',
19428 getAutoCreate : function()
19430 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19431 align = align || 'left'; // default...
19438 tag : this.inline ? 'span' : 'div',
19443 var inline = this.inline ? ' radio-inline' : '';
19447 // does not need for, as we wrap the input with it..
19449 cls : 'control-label box-label' + inline,
19452 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19456 //cls : 'control-label' + inline,
19457 html : this.fieldLabel,
19458 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19467 type : this.inputType,
19468 //value : (!this.checked) ? this.valueOff : this.inputValue,
19469 value : this.inputValue,
19471 placeholder : this.placeholder || '' // ?? needed????
19474 if (this.weight) { // Validity check?
19475 input.cls += " radio-" + this.weight;
19477 if (this.disabled) {
19478 input.disabled=true;
19482 input.checked = this.checked;
19486 input.name = this.name;
19490 input.cls += ' input-' + this.size;
19493 //?? can span's inline have a width??
19496 ['xs','sm','md','lg'].map(function(size){
19497 if (settings[size]) {
19498 cfg.cls += ' col-' + size + '-' + settings[size];
19502 var inputblock = input;
19504 if (this.before || this.after) {
19507 cls : 'input-group',
19512 inputblock.cn.push({
19514 cls : 'input-group-addon',
19518 inputblock.cn.push(input);
19520 inputblock.cn.push({
19522 cls : 'input-group-addon',
19530 if (this.fieldLabel && this.fieldLabel.length) {
19531 cfg.cn.push(fieldLabel);
19534 // normal bootstrap puts the input inside the label.
19535 // however with our styled version - it has to go after the input.
19537 //lbl.cn.push(inputblock);
19541 cls: 'radio' + inline,
19548 cfg.cn.push( lblwrap);
19553 html: this.boxLabel
19562 initEvents : function()
19564 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19566 this.inputEl().on('click', this.onClick, this);
19567 if (this.boxLabel) {
19568 //Roo.log('find label');
19569 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19574 inputEl: function ()
19576 return this.el.select('input.roo-radio',true).first();
19578 onClick : function()
19581 this.setChecked(true);
19584 setChecked : function(state,suppressEvent)
19587 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19588 v.dom.checked = false;
19591 Roo.log(this.inputEl().dom);
19592 this.checked = state;
19593 this.inputEl().dom.checked = state;
19595 if(suppressEvent !== true){
19596 this.fireEvent('check', this, state);
19599 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19603 getGroupValue : function()
19606 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19607 if(v.dom.checked == true){
19608 value = v.dom.value;
19616 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19617 * @return {Mixed} value The field value
19619 getValue : function(){
19620 return this.getGroupValue();
19626 //<script type="text/javascript">
19629 * Based Ext JS Library 1.1.1
19630 * Copyright(c) 2006-2007, Ext JS, LLC.
19636 * @class Roo.HtmlEditorCore
19637 * @extends Roo.Component
19638 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19640 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19643 Roo.HtmlEditorCore = function(config){
19646 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19651 * @event initialize
19652 * Fires when the editor is fully initialized (including the iframe)
19653 * @param {Roo.HtmlEditorCore} this
19658 * Fires when the editor is first receives the focus. Any insertion must wait
19659 * until after this event.
19660 * @param {Roo.HtmlEditorCore} this
19664 * @event beforesync
19665 * Fires before the textarea is updated with content from the editor iframe. Return false
19666 * to cancel the sync.
19667 * @param {Roo.HtmlEditorCore} this
19668 * @param {String} html
19672 * @event beforepush
19673 * Fires before the iframe editor is updated with content from the textarea. Return false
19674 * to cancel the push.
19675 * @param {Roo.HtmlEditorCore} this
19676 * @param {String} html
19681 * Fires when the textarea is updated with content from the editor iframe.
19682 * @param {Roo.HtmlEditorCore} this
19683 * @param {String} html
19688 * Fires when the iframe editor is updated with content from the textarea.
19689 * @param {Roo.HtmlEditorCore} this
19690 * @param {String} html
19695 * @event editorevent
19696 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19697 * @param {Roo.HtmlEditorCore} this
19703 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19705 // defaults : white / black...
19706 this.applyBlacklists();
19713 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19717 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19723 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19728 * @cfg {Number} height (in pixels)
19732 * @cfg {Number} width (in pixels)
19737 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19740 stylesheets: false,
19745 // private properties
19746 validationEvent : false,
19748 initialized : false,
19750 sourceEditMode : false,
19751 onFocus : Roo.emptyFn,
19753 hideMode:'offsets',
19757 // blacklist + whitelisted elements..
19764 * Protected method that will not generally be called directly. It
19765 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19766 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19768 getDocMarkup : function(){
19772 // inherit styels from page...??
19773 if (this.stylesheets === false) {
19775 Roo.get(document.head).select('style').each(function(node) {
19776 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19779 Roo.get(document.head).select('link').each(function(node) {
19780 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19783 } else if (!this.stylesheets.length) {
19785 st = '<style type="text/css">' +
19786 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19792 st += '<style type="text/css">' +
19793 'IMG { cursor: pointer } ' +
19797 return '<html><head>' + st +
19798 //<style type="text/css">' +
19799 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19801 ' </head><body class="roo-htmleditor-body"></body></html>';
19805 onRender : function(ct, position)
19808 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19809 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19812 this.el.dom.style.border = '0 none';
19813 this.el.dom.setAttribute('tabIndex', -1);
19814 this.el.addClass('x-hidden hide');
19818 if(Roo.isIE){ // fix IE 1px bogus margin
19819 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19823 this.frameId = Roo.id();
19827 var iframe = this.owner.wrap.createChild({
19829 cls: 'form-control', // bootstrap..
19831 name: this.frameId,
19832 frameBorder : 'no',
19833 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19838 this.iframe = iframe.dom;
19840 this.assignDocWin();
19842 this.doc.designMode = 'on';
19845 this.doc.write(this.getDocMarkup());
19849 var task = { // must defer to wait for browser to be ready
19851 //console.log("run task?" + this.doc.readyState);
19852 this.assignDocWin();
19853 if(this.doc.body || this.doc.readyState == 'complete'){
19855 this.doc.designMode="on";
19859 Roo.TaskMgr.stop(task);
19860 this.initEditor.defer(10, this);
19867 Roo.TaskMgr.start(task);
19872 onResize : function(w, h)
19874 Roo.log('resize: ' +w + ',' + h );
19875 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19879 if(typeof w == 'number'){
19881 this.iframe.style.width = w + 'px';
19883 if(typeof h == 'number'){
19885 this.iframe.style.height = h + 'px';
19887 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19894 * Toggles the editor between standard and source edit mode.
19895 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19897 toggleSourceEdit : function(sourceEditMode){
19899 this.sourceEditMode = sourceEditMode === true;
19901 if(this.sourceEditMode){
19903 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19906 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19907 //this.iframe.className = '';
19910 //this.setSize(this.owner.wrap.getSize());
19911 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19918 * Protected method that will not generally be called directly. If you need/want
19919 * custom HTML cleanup, this is the method you should override.
19920 * @param {String} html The HTML to be cleaned
19921 * return {String} The cleaned HTML
19923 cleanHtml : function(html){
19924 html = String(html);
19925 if(html.length > 5){
19926 if(Roo.isSafari){ // strip safari nonsense
19927 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19930 if(html == ' '){
19937 * HTML Editor -> Textarea
19938 * Protected method that will not generally be called directly. Syncs the contents
19939 * of the editor iframe with the textarea.
19941 syncValue : function(){
19942 if(this.initialized){
19943 var bd = (this.doc.body || this.doc.documentElement);
19944 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19945 var html = bd.innerHTML;
19947 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19948 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19950 html = '<div style="'+m[0]+'">' + html + '</div>';
19953 html = this.cleanHtml(html);
19954 // fix up the special chars.. normaly like back quotes in word...
19955 // however we do not want to do this with chinese..
19956 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19957 var cc = b.charCodeAt();
19959 (cc >= 0x4E00 && cc < 0xA000 ) ||
19960 (cc >= 0x3400 && cc < 0x4E00 ) ||
19961 (cc >= 0xf900 && cc < 0xfb00 )
19967 if(this.owner.fireEvent('beforesync', this, html) !== false){
19968 this.el.dom.value = html;
19969 this.owner.fireEvent('sync', this, html);
19975 * Protected method that will not generally be called directly. Pushes the value of the textarea
19976 * into the iframe editor.
19978 pushValue : function(){
19979 if(this.initialized){
19980 var v = this.el.dom.value.trim();
19982 // if(v.length < 1){
19986 if(this.owner.fireEvent('beforepush', this, v) !== false){
19987 var d = (this.doc.body || this.doc.documentElement);
19989 this.cleanUpPaste();
19990 this.el.dom.value = d.innerHTML;
19991 this.owner.fireEvent('push', this, v);
19997 deferFocus : function(){
19998 this.focus.defer(10, this);
20002 focus : function(){
20003 if(this.win && !this.sourceEditMode){
20010 assignDocWin: function()
20012 var iframe = this.iframe;
20015 this.doc = iframe.contentWindow.document;
20016 this.win = iframe.contentWindow;
20018 // if (!Roo.get(this.frameId)) {
20021 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20022 // this.win = Roo.get(this.frameId).dom.contentWindow;
20024 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20028 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20029 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20034 initEditor : function(){
20035 //console.log("INIT EDITOR");
20036 this.assignDocWin();
20040 this.doc.designMode="on";
20042 this.doc.write(this.getDocMarkup());
20045 var dbody = (this.doc.body || this.doc.documentElement);
20046 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20047 // this copies styles from the containing element into thsi one..
20048 // not sure why we need all of this..
20049 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20051 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20052 //ss['background-attachment'] = 'fixed'; // w3c
20053 dbody.bgProperties = 'fixed'; // ie
20054 //Roo.DomHelper.applyStyles(dbody, ss);
20055 Roo.EventManager.on(this.doc, {
20056 //'mousedown': this.onEditorEvent,
20057 'mouseup': this.onEditorEvent,
20058 'dblclick': this.onEditorEvent,
20059 'click': this.onEditorEvent,
20060 'keyup': this.onEditorEvent,
20065 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20067 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20068 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20070 this.initialized = true;
20072 this.owner.fireEvent('initialize', this);
20077 onDestroy : function(){
20083 //for (var i =0; i < this.toolbars.length;i++) {
20084 // // fixme - ask toolbars for heights?
20085 // this.toolbars[i].onDestroy();
20088 //this.wrap.dom.innerHTML = '';
20089 //this.wrap.remove();
20094 onFirstFocus : function(){
20096 this.assignDocWin();
20099 this.activated = true;
20102 if(Roo.isGecko){ // prevent silly gecko errors
20104 var s = this.win.getSelection();
20105 if(!s.focusNode || s.focusNode.nodeType != 3){
20106 var r = s.getRangeAt(0);
20107 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20112 this.execCmd('useCSS', true);
20113 this.execCmd('styleWithCSS', false);
20116 this.owner.fireEvent('activate', this);
20120 adjustFont: function(btn){
20121 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20122 //if(Roo.isSafari){ // safari
20125 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20126 if(Roo.isSafari){ // safari
20127 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20128 v = (v < 10) ? 10 : v;
20129 v = (v > 48) ? 48 : v;
20130 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20135 v = Math.max(1, v+adjust);
20137 this.execCmd('FontSize', v );
20140 onEditorEvent : function(e)
20142 this.owner.fireEvent('editorevent', this, e);
20143 // this.updateToolbar();
20144 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20147 insertTag : function(tg)
20149 // could be a bit smarter... -> wrap the current selected tRoo..
20150 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20152 range = this.createRange(this.getSelection());
20153 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20154 wrappingNode.appendChild(range.extractContents());
20155 range.insertNode(wrappingNode);
20162 this.execCmd("formatblock", tg);
20166 insertText : function(txt)
20170 var range = this.createRange();
20171 range.deleteContents();
20172 //alert(Sender.getAttribute('label'));
20174 range.insertNode(this.doc.createTextNode(txt));
20180 * Executes a Midas editor command on the editor document and performs necessary focus and
20181 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20182 * @param {String} cmd The Midas command
20183 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20185 relayCmd : function(cmd, value){
20187 this.execCmd(cmd, value);
20188 this.owner.fireEvent('editorevent', this);
20189 //this.updateToolbar();
20190 this.owner.deferFocus();
20194 * Executes a Midas editor command directly on the editor document.
20195 * For visual commands, you should use {@link #relayCmd} instead.
20196 * <b>This should only be called after the editor is initialized.</b>
20197 * @param {String} cmd The Midas command
20198 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20200 execCmd : function(cmd, value){
20201 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20208 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20210 * @param {String} text | dom node..
20212 insertAtCursor : function(text)
20217 if(!this.activated){
20223 var r = this.doc.selection.createRange();
20234 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20238 // from jquery ui (MIT licenced)
20240 var win = this.win;
20242 if (win.getSelection && win.getSelection().getRangeAt) {
20243 range = win.getSelection().getRangeAt(0);
20244 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20245 range.insertNode(node);
20246 } else if (win.document.selection && win.document.selection.createRange) {
20247 // no firefox support
20248 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20249 win.document.selection.createRange().pasteHTML(txt);
20251 // no firefox support
20252 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20253 this.execCmd('InsertHTML', txt);
20262 mozKeyPress : function(e){
20264 var c = e.getCharCode(), cmd;
20267 c = String.fromCharCode(c).toLowerCase();
20281 this.cleanUpPaste.defer(100, this);
20289 e.preventDefault();
20297 fixKeys : function(){ // load time branching for fastest keydown performance
20299 return function(e){
20300 var k = e.getKey(), r;
20303 r = this.doc.selection.createRange();
20306 r.pasteHTML('    ');
20313 r = this.doc.selection.createRange();
20315 var target = r.parentElement();
20316 if(!target || target.tagName.toLowerCase() != 'li'){
20318 r.pasteHTML('<br />');
20324 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20325 this.cleanUpPaste.defer(100, this);
20331 }else if(Roo.isOpera){
20332 return function(e){
20333 var k = e.getKey();
20337 this.execCmd('InsertHTML','    ');
20340 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20341 this.cleanUpPaste.defer(100, this);
20346 }else if(Roo.isSafari){
20347 return function(e){
20348 var k = e.getKey();
20352 this.execCmd('InsertText','\t');
20356 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20357 this.cleanUpPaste.defer(100, this);
20365 getAllAncestors: function()
20367 var p = this.getSelectedNode();
20370 a.push(p); // push blank onto stack..
20371 p = this.getParentElement();
20375 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20379 a.push(this.doc.body);
20383 lastSelNode : false,
20386 getSelection : function()
20388 this.assignDocWin();
20389 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20392 getSelectedNode: function()
20394 // this may only work on Gecko!!!
20396 // should we cache this!!!!
20401 var range = this.createRange(this.getSelection()).cloneRange();
20404 var parent = range.parentElement();
20406 var testRange = range.duplicate();
20407 testRange.moveToElementText(parent);
20408 if (testRange.inRange(range)) {
20411 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20414 parent = parent.parentElement;
20419 // is ancestor a text element.
20420 var ac = range.commonAncestorContainer;
20421 if (ac.nodeType == 3) {
20422 ac = ac.parentNode;
20425 var ar = ac.childNodes;
20428 var other_nodes = [];
20429 var has_other_nodes = false;
20430 for (var i=0;i<ar.length;i++) {
20431 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20434 // fullly contained node.
20436 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20441 // probably selected..
20442 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20443 other_nodes.push(ar[i]);
20447 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20452 has_other_nodes = true;
20454 if (!nodes.length && other_nodes.length) {
20455 nodes= other_nodes;
20457 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20463 createRange: function(sel)
20465 // this has strange effects when using with
20466 // top toolbar - not sure if it's a great idea.
20467 //this.editor.contentWindow.focus();
20468 if (typeof sel != "undefined") {
20470 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20472 return this.doc.createRange();
20475 return this.doc.createRange();
20478 getParentElement: function()
20481 this.assignDocWin();
20482 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20484 var range = this.createRange(sel);
20487 var p = range.commonAncestorContainer;
20488 while (p.nodeType == 3) { // text node
20499 * Range intersection.. the hard stuff...
20503 * [ -- selected range --- ]
20507 * if end is before start or hits it. fail.
20508 * if start is after end or hits it fail.
20510 * if either hits (but other is outside. - then it's not
20516 // @see http://www.thismuchiknow.co.uk/?p=64.
20517 rangeIntersectsNode : function(range, node)
20519 var nodeRange = node.ownerDocument.createRange();
20521 nodeRange.selectNode(node);
20523 nodeRange.selectNodeContents(node);
20526 var rangeStartRange = range.cloneRange();
20527 rangeStartRange.collapse(true);
20529 var rangeEndRange = range.cloneRange();
20530 rangeEndRange.collapse(false);
20532 var nodeStartRange = nodeRange.cloneRange();
20533 nodeStartRange.collapse(true);
20535 var nodeEndRange = nodeRange.cloneRange();
20536 nodeEndRange.collapse(false);
20538 return rangeStartRange.compareBoundaryPoints(
20539 Range.START_TO_START, nodeEndRange) == -1 &&
20540 rangeEndRange.compareBoundaryPoints(
20541 Range.START_TO_START, nodeStartRange) == 1;
20545 rangeCompareNode : function(range, node)
20547 var nodeRange = node.ownerDocument.createRange();
20549 nodeRange.selectNode(node);
20551 nodeRange.selectNodeContents(node);
20555 range.collapse(true);
20557 nodeRange.collapse(true);
20559 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20560 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20562 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20564 var nodeIsBefore = ss == 1;
20565 var nodeIsAfter = ee == -1;
20567 if (nodeIsBefore && nodeIsAfter) {
20570 if (!nodeIsBefore && nodeIsAfter) {
20571 return 1; //right trailed.
20574 if (nodeIsBefore && !nodeIsAfter) {
20575 return 2; // left trailed.
20581 // private? - in a new class?
20582 cleanUpPaste : function()
20584 // cleans up the whole document..
20585 Roo.log('cleanuppaste');
20587 this.cleanUpChildren(this.doc.body);
20588 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20589 if (clean != this.doc.body.innerHTML) {
20590 this.doc.body.innerHTML = clean;
20595 cleanWordChars : function(input) {// change the chars to hex code
20596 var he = Roo.HtmlEditorCore;
20598 var output = input;
20599 Roo.each(he.swapCodes, function(sw) {
20600 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20602 output = output.replace(swapper, sw[1]);
20609 cleanUpChildren : function (n)
20611 if (!n.childNodes.length) {
20614 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20615 this.cleanUpChild(n.childNodes[i]);
20622 cleanUpChild : function (node)
20625 //console.log(node);
20626 if (node.nodeName == "#text") {
20627 // clean up silly Windows -- stuff?
20630 if (node.nodeName == "#comment") {
20631 node.parentNode.removeChild(node);
20632 // clean up silly Windows -- stuff?
20635 var lcname = node.tagName.toLowerCase();
20636 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20637 // whitelist of tags..
20639 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20641 node.parentNode.removeChild(node);
20646 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20648 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20649 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20651 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20652 // remove_keep_children = true;
20655 if (remove_keep_children) {
20656 this.cleanUpChildren(node);
20657 // inserts everything just before this node...
20658 while (node.childNodes.length) {
20659 var cn = node.childNodes[0];
20660 node.removeChild(cn);
20661 node.parentNode.insertBefore(cn, node);
20663 node.parentNode.removeChild(node);
20667 if (!node.attributes || !node.attributes.length) {
20668 this.cleanUpChildren(node);
20672 function cleanAttr(n,v)
20675 if (v.match(/^\./) || v.match(/^\//)) {
20678 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20681 if (v.match(/^#/)) {
20684 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20685 node.removeAttribute(n);
20689 var cwhite = this.cwhite;
20690 var cblack = this.cblack;
20692 function cleanStyle(n,v)
20694 if (v.match(/expression/)) { //XSS?? should we even bother..
20695 node.removeAttribute(n);
20699 var parts = v.split(/;/);
20702 Roo.each(parts, function(p) {
20703 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20707 var l = p.split(':').shift().replace(/\s+/g,'');
20708 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20710 if ( cwhite.length && cblack.indexOf(l) > -1) {
20711 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20712 //node.removeAttribute(n);
20716 // only allow 'c whitelisted system attributes'
20717 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20718 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20719 //node.removeAttribute(n);
20729 if (clean.length) {
20730 node.setAttribute(n, clean.join(';'));
20732 node.removeAttribute(n);
20738 for (var i = node.attributes.length-1; i > -1 ; i--) {
20739 var a = node.attributes[i];
20742 if (a.name.toLowerCase().substr(0,2)=='on') {
20743 node.removeAttribute(a.name);
20746 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20747 node.removeAttribute(a.name);
20750 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20751 cleanAttr(a.name,a.value); // fixme..
20754 if (a.name == 'style') {
20755 cleanStyle(a.name,a.value);
20758 /// clean up MS crap..
20759 // tecnically this should be a list of valid class'es..
20762 if (a.name == 'class') {
20763 if (a.value.match(/^Mso/)) {
20764 node.className = '';
20767 if (a.value.match(/body/)) {
20768 node.className = '';
20779 this.cleanUpChildren(node);
20785 * Clean up MS wordisms...
20787 cleanWord : function(node)
20792 this.cleanWord(this.doc.body);
20795 if (node.nodeName == "#text") {
20796 // clean up silly Windows -- stuff?
20799 if (node.nodeName == "#comment") {
20800 node.parentNode.removeChild(node);
20801 // clean up silly Windows -- stuff?
20805 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20806 node.parentNode.removeChild(node);
20810 // remove - but keep children..
20811 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20812 while (node.childNodes.length) {
20813 var cn = node.childNodes[0];
20814 node.removeChild(cn);
20815 node.parentNode.insertBefore(cn, node);
20817 node.parentNode.removeChild(node);
20818 this.iterateChildren(node, this.cleanWord);
20822 if (node.className.length) {
20824 var cn = node.className.split(/\W+/);
20826 Roo.each(cn, function(cls) {
20827 if (cls.match(/Mso[a-zA-Z]+/)) {
20832 node.className = cna.length ? cna.join(' ') : '';
20834 node.removeAttribute("class");
20838 if (node.hasAttribute("lang")) {
20839 node.removeAttribute("lang");
20842 if (node.hasAttribute("style")) {
20844 var styles = node.getAttribute("style").split(";");
20846 Roo.each(styles, function(s) {
20847 if (!s.match(/:/)) {
20850 var kv = s.split(":");
20851 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20854 // what ever is left... we allow.
20857 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20858 if (!nstyle.length) {
20859 node.removeAttribute('style');
20862 this.iterateChildren(node, this.cleanWord);
20868 * iterateChildren of a Node, calling fn each time, using this as the scole..
20869 * @param {DomNode} node node to iterate children of.
20870 * @param {Function} fn method of this class to call on each item.
20872 iterateChildren : function(node, fn)
20874 if (!node.childNodes.length) {
20877 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20878 fn.call(this, node.childNodes[i])
20884 * cleanTableWidths.
20886 * Quite often pasting from word etc.. results in tables with column and widths.
20887 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20890 cleanTableWidths : function(node)
20895 this.cleanTableWidths(this.doc.body);
20900 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20903 Roo.log(node.tagName);
20904 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20905 this.iterateChildren(node, this.cleanTableWidths);
20908 if (node.hasAttribute('width')) {
20909 node.removeAttribute('width');
20913 if (node.hasAttribute("style")) {
20916 var styles = node.getAttribute("style").split(";");
20918 Roo.each(styles, function(s) {
20919 if (!s.match(/:/)) {
20922 var kv = s.split(":");
20923 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20926 // what ever is left... we allow.
20929 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20930 if (!nstyle.length) {
20931 node.removeAttribute('style');
20935 this.iterateChildren(node, this.cleanTableWidths);
20943 domToHTML : function(currentElement, depth, nopadtext) {
20945 depth = depth || 0;
20946 nopadtext = nopadtext || false;
20948 if (!currentElement) {
20949 return this.domToHTML(this.doc.body);
20952 //Roo.log(currentElement);
20954 var allText = false;
20955 var nodeName = currentElement.nodeName;
20956 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20958 if (nodeName == '#text') {
20960 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20965 if (nodeName != 'BODY') {
20968 // Prints the node tagName, such as <A>, <IMG>, etc
20971 for(i = 0; i < currentElement.attributes.length;i++) {
20973 var aname = currentElement.attributes.item(i).name;
20974 if (!currentElement.attributes.item(i).value.length) {
20977 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20980 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20989 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20992 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20997 // Traverse the tree
20999 var currentElementChild = currentElement.childNodes.item(i);
21000 var allText = true;
21001 var innerHTML = '';
21003 while (currentElementChild) {
21004 // Formatting code (indent the tree so it looks nice on the screen)
21005 var nopad = nopadtext;
21006 if (lastnode == 'SPAN') {
21010 if (currentElementChild.nodeName == '#text') {
21011 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21012 toadd = nopadtext ? toadd : toadd.trim();
21013 if (!nopad && toadd.length > 80) {
21014 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21016 innerHTML += toadd;
21019 currentElementChild = currentElement.childNodes.item(i);
21025 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21027 // Recursively traverse the tree structure of the child node
21028 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21029 lastnode = currentElementChild.nodeName;
21031 currentElementChild=currentElement.childNodes.item(i);
21037 // The remaining code is mostly for formatting the tree
21038 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21043 ret+= "</"+tagName+">";
21049 applyBlacklists : function()
21051 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21052 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21056 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21057 if (b.indexOf(tag) > -1) {
21060 this.white.push(tag);
21064 Roo.each(w, function(tag) {
21065 if (b.indexOf(tag) > -1) {
21068 if (this.white.indexOf(tag) > -1) {
21071 this.white.push(tag);
21076 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21077 if (w.indexOf(tag) > -1) {
21080 this.black.push(tag);
21084 Roo.each(b, function(tag) {
21085 if (w.indexOf(tag) > -1) {
21088 if (this.black.indexOf(tag) > -1) {
21091 this.black.push(tag);
21096 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21097 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21101 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21102 if (b.indexOf(tag) > -1) {
21105 this.cwhite.push(tag);
21109 Roo.each(w, function(tag) {
21110 if (b.indexOf(tag) > -1) {
21113 if (this.cwhite.indexOf(tag) > -1) {
21116 this.cwhite.push(tag);
21121 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21122 if (w.indexOf(tag) > -1) {
21125 this.cblack.push(tag);
21129 Roo.each(b, function(tag) {
21130 if (w.indexOf(tag) > -1) {
21133 if (this.cblack.indexOf(tag) > -1) {
21136 this.cblack.push(tag);
21141 setStylesheets : function(stylesheets)
21143 if(typeof(stylesheets) == 'string'){
21144 Roo.get(this.iframe.contentDocument.head).createChild({
21146 rel : 'stylesheet',
21155 Roo.each(stylesheets, function(s) {
21160 Roo.get(_this.iframe.contentDocument.head).createChild({
21162 rel : 'stylesheet',
21171 removeStylesheets : function()
21175 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21180 // hide stuff that is not compatible
21194 * @event specialkey
21198 * @cfg {String} fieldClass @hide
21201 * @cfg {String} focusClass @hide
21204 * @cfg {String} autoCreate @hide
21207 * @cfg {String} inputType @hide
21210 * @cfg {String} invalidClass @hide
21213 * @cfg {String} invalidText @hide
21216 * @cfg {String} msgFx @hide
21219 * @cfg {String} validateOnBlur @hide
21223 Roo.HtmlEditorCore.white = [
21224 'area', 'br', 'img', 'input', 'hr', 'wbr',
21226 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21227 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21228 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21229 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21230 'table', 'ul', 'xmp',
21232 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21235 'dir', 'menu', 'ol', 'ul', 'dl',
21241 Roo.HtmlEditorCore.black = [
21242 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21244 'base', 'basefont', 'bgsound', 'blink', 'body',
21245 'frame', 'frameset', 'head', 'html', 'ilayer',
21246 'iframe', 'layer', 'link', 'meta', 'object',
21247 'script', 'style' ,'title', 'xml' // clean later..
21249 Roo.HtmlEditorCore.clean = [
21250 'script', 'style', 'title', 'xml'
21252 Roo.HtmlEditorCore.remove = [
21257 Roo.HtmlEditorCore.ablack = [
21261 Roo.HtmlEditorCore.aclean = [
21262 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21266 Roo.HtmlEditorCore.pwhite= [
21267 'http', 'https', 'mailto'
21270 // white listed style attributes.
21271 Roo.HtmlEditorCore.cwhite= [
21272 // 'text-align', /// default is to allow most things..
21278 // black listed style attributes.
21279 Roo.HtmlEditorCore.cblack= [
21280 // 'font-size' -- this can be set by the project
21284 Roo.HtmlEditorCore.swapCodes =[
21303 * @class Roo.bootstrap.HtmlEditor
21304 * @extends Roo.bootstrap.TextArea
21305 * Bootstrap HtmlEditor class
21308 * Create a new HtmlEditor
21309 * @param {Object} config The config object
21312 Roo.bootstrap.HtmlEditor = function(config){
21313 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21314 if (!this.toolbars) {
21315 this.toolbars = [];
21317 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21320 * @event initialize
21321 * Fires when the editor is fully initialized (including the iframe)
21322 * @param {HtmlEditor} this
21327 * Fires when the editor is first receives the focus. Any insertion must wait
21328 * until after this event.
21329 * @param {HtmlEditor} this
21333 * @event beforesync
21334 * Fires before the textarea is updated with content from the editor iframe. Return false
21335 * to cancel the sync.
21336 * @param {HtmlEditor} this
21337 * @param {String} html
21341 * @event beforepush
21342 * Fires before the iframe editor is updated with content from the textarea. Return false
21343 * to cancel the push.
21344 * @param {HtmlEditor} this
21345 * @param {String} html
21350 * Fires when the textarea is updated with content from the editor iframe.
21351 * @param {HtmlEditor} this
21352 * @param {String} html
21357 * Fires when the iframe editor is updated with content from the textarea.
21358 * @param {HtmlEditor} this
21359 * @param {String} html
21363 * @event editmodechange
21364 * Fires when the editor switches edit modes
21365 * @param {HtmlEditor} this
21366 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21368 editmodechange: true,
21370 * @event editorevent
21371 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21372 * @param {HtmlEditor} this
21376 * @event firstfocus
21377 * Fires when on first focus - needed by toolbars..
21378 * @param {HtmlEditor} this
21383 * Auto save the htmlEditor value as a file into Events
21384 * @param {HtmlEditor} this
21388 * @event savedpreview
21389 * preview the saved version of htmlEditor
21390 * @param {HtmlEditor} this
21397 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21401 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21406 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21411 * @cfg {Number} height (in pixels)
21415 * @cfg {Number} width (in pixels)
21420 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21423 stylesheets: false,
21428 // private properties
21429 validationEvent : false,
21431 initialized : false,
21434 onFocus : Roo.emptyFn,
21436 hideMode:'offsets',
21439 tbContainer : false,
21441 toolbarContainer :function() {
21442 return this.wrap.select('.x-html-editor-tb',true).first();
21446 * Protected method that will not generally be called directly. It
21447 * is called when the editor creates its toolbar. Override this method if you need to
21448 * add custom toolbar buttons.
21449 * @param {HtmlEditor} editor
21451 createToolbar : function(){
21453 Roo.log("create toolbars");
21455 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21456 this.toolbars[0].render(this.toolbarContainer());
21460 // if (!editor.toolbars || !editor.toolbars.length) {
21461 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21464 // for (var i =0 ; i < editor.toolbars.length;i++) {
21465 // editor.toolbars[i] = Roo.factory(
21466 // typeof(editor.toolbars[i]) == 'string' ?
21467 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21468 // Roo.bootstrap.HtmlEditor);
21469 // editor.toolbars[i].init(editor);
21475 onRender : function(ct, position)
21477 // Roo.log("Call onRender: " + this.xtype);
21479 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21481 this.wrap = this.inputEl().wrap({
21482 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21485 this.editorcore.onRender(ct, position);
21487 if (this.resizable) {
21488 this.resizeEl = new Roo.Resizable(this.wrap, {
21492 minHeight : this.height,
21493 height: this.height,
21494 handles : this.resizable,
21497 resize : function(r, w, h) {
21498 _t.onResize(w,h); // -something
21504 this.createToolbar(this);
21507 if(!this.width && this.resizable){
21508 this.setSize(this.wrap.getSize());
21510 if (this.resizeEl) {
21511 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21512 // should trigger onReize..
21518 onResize : function(w, h)
21520 Roo.log('resize: ' +w + ',' + h );
21521 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21525 if(this.inputEl() ){
21526 if(typeof w == 'number'){
21527 var aw = w - this.wrap.getFrameWidth('lr');
21528 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21531 if(typeof h == 'number'){
21532 var tbh = -11; // fixme it needs to tool bar size!
21533 for (var i =0; i < this.toolbars.length;i++) {
21534 // fixme - ask toolbars for heights?
21535 tbh += this.toolbars[i].el.getHeight();
21536 //if (this.toolbars[i].footer) {
21537 // tbh += this.toolbars[i].footer.el.getHeight();
21545 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21546 ah -= 5; // knock a few pixes off for look..
21547 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21551 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21552 this.editorcore.onResize(ew,eh);
21557 * Toggles the editor between standard and source edit mode.
21558 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21560 toggleSourceEdit : function(sourceEditMode)
21562 this.editorcore.toggleSourceEdit(sourceEditMode);
21564 if(this.editorcore.sourceEditMode){
21565 Roo.log('editor - showing textarea');
21568 // Roo.log(this.syncValue());
21570 this.inputEl().removeClass(['hide', 'x-hidden']);
21571 this.inputEl().dom.removeAttribute('tabIndex');
21572 this.inputEl().focus();
21574 Roo.log('editor - hiding textarea');
21576 // Roo.log(this.pushValue());
21579 this.inputEl().addClass(['hide', 'x-hidden']);
21580 this.inputEl().dom.setAttribute('tabIndex', -1);
21581 //this.deferFocus();
21584 if(this.resizable){
21585 this.setSize(this.wrap.getSize());
21588 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21591 // private (for BoxComponent)
21592 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21594 // private (for BoxComponent)
21595 getResizeEl : function(){
21599 // private (for BoxComponent)
21600 getPositionEl : function(){
21605 initEvents : function(){
21606 this.originalValue = this.getValue();
21610 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21613 // markInvalid : Roo.emptyFn,
21615 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21618 // clearInvalid : Roo.emptyFn,
21620 setValue : function(v){
21621 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21622 this.editorcore.pushValue();
21627 deferFocus : function(){
21628 this.focus.defer(10, this);
21632 focus : function(){
21633 this.editorcore.focus();
21639 onDestroy : function(){
21645 for (var i =0; i < this.toolbars.length;i++) {
21646 // fixme - ask toolbars for heights?
21647 this.toolbars[i].onDestroy();
21650 this.wrap.dom.innerHTML = '';
21651 this.wrap.remove();
21656 onFirstFocus : function(){
21657 //Roo.log("onFirstFocus");
21658 this.editorcore.onFirstFocus();
21659 for (var i =0; i < this.toolbars.length;i++) {
21660 this.toolbars[i].onFirstFocus();
21666 syncValue : function()
21668 this.editorcore.syncValue();
21671 pushValue : function()
21673 this.editorcore.pushValue();
21677 // hide stuff that is not compatible
21691 * @event specialkey
21695 * @cfg {String} fieldClass @hide
21698 * @cfg {String} focusClass @hide
21701 * @cfg {String} autoCreate @hide
21704 * @cfg {String} inputType @hide
21707 * @cfg {String} invalidClass @hide
21710 * @cfg {String} invalidText @hide
21713 * @cfg {String} msgFx @hide
21716 * @cfg {String} validateOnBlur @hide
21725 Roo.namespace('Roo.bootstrap.htmleditor');
21727 * @class Roo.bootstrap.HtmlEditorToolbar1
21732 new Roo.bootstrap.HtmlEditor({
21735 new Roo.bootstrap.HtmlEditorToolbar1({
21736 disable : { fonts: 1 , format: 1, ..., ... , ...],
21742 * @cfg {Object} disable List of elements to disable..
21743 * @cfg {Array} btns List of additional buttons.
21747 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21750 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21753 Roo.apply(this, config);
21755 // default disabled, based on 'good practice'..
21756 this.disable = this.disable || {};
21757 Roo.applyIf(this.disable, {
21760 specialElements : true
21762 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21764 this.editor = config.editor;
21765 this.editorcore = config.editor.editorcore;
21767 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21769 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21770 // dont call parent... till later.
21772 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21777 editorcore : false,
21782 "h1","h2","h3","h4","h5","h6",
21784 "abbr", "acronym", "address", "cite", "samp", "var",
21788 onRender : function(ct, position)
21790 // Roo.log("Call onRender: " + this.xtype);
21792 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21794 this.el.dom.style.marginBottom = '0';
21796 var editorcore = this.editorcore;
21797 var editor= this.editor;
21800 var btn = function(id,cmd , toggle, handler){
21802 var event = toggle ? 'toggle' : 'click';
21807 xns: Roo.bootstrap,
21810 enableToggle:toggle !== false,
21812 pressed : toggle ? false : null,
21815 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21816 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21825 xns: Roo.bootstrap,
21826 glyphicon : 'font',
21830 xns: Roo.bootstrap,
21834 Roo.each(this.formats, function(f) {
21835 style.menu.items.push({
21837 xns: Roo.bootstrap,
21838 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21843 editorcore.insertTag(this.tagname);
21850 children.push(style);
21853 btn('bold',false,true);
21854 btn('italic',false,true);
21855 btn('align-left', 'justifyleft',true);
21856 btn('align-center', 'justifycenter',true);
21857 btn('align-right' , 'justifyright',true);
21858 btn('link', false, false, function(btn) {
21859 //Roo.log("create link?");
21860 var url = prompt(this.createLinkText, this.defaultLinkValue);
21861 if(url && url != 'http:/'+'/'){
21862 this.editorcore.relayCmd('createlink', url);
21865 btn('list','insertunorderedlist',true);
21866 btn('pencil', false,true, function(btn){
21869 this.toggleSourceEdit(btn.pressed);
21875 xns: Roo.bootstrap,
21880 xns: Roo.bootstrap,
21885 cog.menu.items.push({
21887 xns: Roo.bootstrap,
21888 html : Clean styles,
21893 editorcore.insertTag(this.tagname);
21902 this.xtype = 'NavSimplebar';
21904 for(var i=0;i< children.length;i++) {
21906 this.buttons.add(this.addxtypeChild(children[i]));
21910 editor.on('editorevent', this.updateToolbar, this);
21912 onBtnClick : function(id)
21914 this.editorcore.relayCmd(id);
21915 this.editorcore.focus();
21919 * Protected method that will not generally be called directly. It triggers
21920 * a toolbar update by reading the markup state of the current selection in the editor.
21922 updateToolbar: function(){
21924 if(!this.editorcore.activated){
21925 this.editor.onFirstFocus(); // is this neeed?
21929 var btns = this.buttons;
21930 var doc = this.editorcore.doc;
21931 btns.get('bold').setActive(doc.queryCommandState('bold'));
21932 btns.get('italic').setActive(doc.queryCommandState('italic'));
21933 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21935 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21936 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21937 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21939 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21940 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21943 var ans = this.editorcore.getAllAncestors();
21944 if (this.formatCombo) {
21947 var store = this.formatCombo.store;
21948 this.formatCombo.setValue("");
21949 for (var i =0; i < ans.length;i++) {
21950 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21952 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21960 // hides menus... - so this cant be on a menu...
21961 Roo.bootstrap.MenuMgr.hideAll();
21963 Roo.bootstrap.MenuMgr.hideAll();
21964 //this.editorsyncValue();
21966 onFirstFocus: function() {
21967 this.buttons.each(function(item){
21971 toggleSourceEdit : function(sourceEditMode){
21974 if(sourceEditMode){
21975 Roo.log("disabling buttons");
21976 this.buttons.each( function(item){
21977 if(item.cmd != 'pencil'){
21983 Roo.log("enabling buttons");
21984 if(this.editorcore.initialized){
21985 this.buttons.each( function(item){
21991 Roo.log("calling toggole on editor");
21992 // tell the editor that it's been pressed..
21993 this.editor.toggleSourceEdit(sourceEditMode);
22003 * @class Roo.bootstrap.Table.AbstractSelectionModel
22004 * @extends Roo.util.Observable
22005 * Abstract base class for grid SelectionModels. It provides the interface that should be
22006 * implemented by descendant classes. This class should not be directly instantiated.
22009 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22010 this.locked = false;
22011 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22015 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22016 /** @ignore Called by the grid automatically. Do not call directly. */
22017 init : function(grid){
22023 * Locks the selections.
22026 this.locked = true;
22030 * Unlocks the selections.
22032 unlock : function(){
22033 this.locked = false;
22037 * Returns true if the selections are locked.
22038 * @return {Boolean}
22040 isLocked : function(){
22041 return this.locked;
22045 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22046 * @class Roo.bootstrap.Table.RowSelectionModel
22047 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22048 * It supports multiple selections and keyboard selection/navigation.
22050 * @param {Object} config
22053 Roo.bootstrap.Table.RowSelectionModel = function(config){
22054 Roo.apply(this, config);
22055 this.selections = new Roo.util.MixedCollection(false, function(o){
22060 this.lastActive = false;
22064 * @event selectionchange
22065 * Fires when the selection changes
22066 * @param {SelectionModel} this
22068 "selectionchange" : true,
22070 * @event afterselectionchange
22071 * Fires after the selection changes (eg. by key press or clicking)
22072 * @param {SelectionModel} this
22074 "afterselectionchange" : true,
22076 * @event beforerowselect
22077 * Fires when a row is selected being selected, return false to cancel.
22078 * @param {SelectionModel} this
22079 * @param {Number} rowIndex The selected index
22080 * @param {Boolean} keepExisting False if other selections will be cleared
22082 "beforerowselect" : true,
22085 * Fires when a row is selected.
22086 * @param {SelectionModel} this
22087 * @param {Number} rowIndex The selected index
22088 * @param {Roo.data.Record} r The record
22090 "rowselect" : true,
22092 * @event rowdeselect
22093 * Fires when a row is deselected.
22094 * @param {SelectionModel} this
22095 * @param {Number} rowIndex The selected index
22097 "rowdeselect" : true
22099 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22100 this.locked = false;
22103 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22105 * @cfg {Boolean} singleSelect
22106 * True to allow selection of only one row at a time (defaults to false)
22108 singleSelect : false,
22111 initEvents : function(){
22113 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22114 this.grid.on("mousedown", this.handleMouseDown, this);
22115 }else{ // allow click to work like normal
22116 this.grid.on("rowclick", this.handleDragableRowClick, this);
22119 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22120 "up" : function(e){
22122 this.selectPrevious(e.shiftKey);
22123 }else if(this.last !== false && this.lastActive !== false){
22124 var last = this.last;
22125 this.selectRange(this.last, this.lastActive-1);
22126 this.grid.getView().focusRow(this.lastActive);
22127 if(last !== false){
22131 this.selectFirstRow();
22133 this.fireEvent("afterselectionchange", this);
22135 "down" : function(e){
22137 this.selectNext(e.shiftKey);
22138 }else if(this.last !== false && this.lastActive !== false){
22139 var last = this.last;
22140 this.selectRange(this.last, this.lastActive+1);
22141 this.grid.getView().focusRow(this.lastActive);
22142 if(last !== false){
22146 this.selectFirstRow();
22148 this.fireEvent("afterselectionchange", this);
22153 var view = this.grid.view;
22154 view.on("refresh", this.onRefresh, this);
22155 view.on("rowupdated", this.onRowUpdated, this);
22156 view.on("rowremoved", this.onRemove, this);
22160 onRefresh : function(){
22161 var ds = this.grid.dataSource, i, v = this.grid.view;
22162 var s = this.selections;
22163 s.each(function(r){
22164 if((i = ds.indexOfId(r.id)) != -1){
22173 onRemove : function(v, index, r){
22174 this.selections.remove(r);
22178 onRowUpdated : function(v, index, r){
22179 if(this.isSelected(r)){
22180 v.onRowSelect(index);
22186 * @param {Array} records The records to select
22187 * @param {Boolean} keepExisting (optional) True to keep existing selections
22189 selectRecords : function(records, keepExisting){
22191 this.clearSelections();
22193 var ds = this.grid.dataSource;
22194 for(var i = 0, len = records.length; i < len; i++){
22195 this.selectRow(ds.indexOf(records[i]), true);
22200 * Gets the number of selected rows.
22203 getCount : function(){
22204 return this.selections.length;
22208 * Selects the first row in the grid.
22210 selectFirstRow : function(){
22215 * Select the last row.
22216 * @param {Boolean} keepExisting (optional) True to keep existing selections
22218 selectLastRow : function(keepExisting){
22219 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22223 * Selects the row immediately following the last selected row.
22224 * @param {Boolean} keepExisting (optional) True to keep existing selections
22226 selectNext : function(keepExisting){
22227 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22228 this.selectRow(this.last+1, keepExisting);
22229 this.grid.getView().focusRow(this.last);
22234 * Selects the row that precedes the last selected row.
22235 * @param {Boolean} keepExisting (optional) True to keep existing selections
22237 selectPrevious : function(keepExisting){
22239 this.selectRow(this.last-1, keepExisting);
22240 this.grid.getView().focusRow(this.last);
22245 * Returns the selected records
22246 * @return {Array} Array of selected records
22248 getSelections : function(){
22249 return [].concat(this.selections.items);
22253 * Returns the first selected record.
22256 getSelected : function(){
22257 return this.selections.itemAt(0);
22262 * Clears all selections.
22264 clearSelections : function(fast){
22269 var ds = this.grid.dataSource;
22270 var s = this.selections;
22271 s.each(function(r){
22272 this.deselectRow(ds.indexOfId(r.id));
22276 this.selections.clear();
22283 * Selects all rows.
22285 selectAll : function(){
22289 this.selections.clear();
22290 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22291 this.selectRow(i, true);
22296 * Returns True if there is a selection.
22297 * @return {Boolean}
22299 hasSelection : function(){
22300 return this.selections.length > 0;
22304 * Returns True if the specified row is selected.
22305 * @param {Number/Record} record The record or index of the record to check
22306 * @return {Boolean}
22308 isSelected : function(index){
22309 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22310 return (r && this.selections.key(r.id) ? true : false);
22314 * Returns True if the specified record id is selected.
22315 * @param {String} id The id of record to check
22316 * @return {Boolean}
22318 isIdSelected : function(id){
22319 return (this.selections.key(id) ? true : false);
22323 handleMouseDown : function(e, t){
22324 var view = this.grid.getView(), rowIndex;
22325 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22328 if(e.shiftKey && this.last !== false){
22329 var last = this.last;
22330 this.selectRange(last, rowIndex, e.ctrlKey);
22331 this.last = last; // reset the last
22332 view.focusRow(rowIndex);
22334 var isSelected = this.isSelected(rowIndex);
22335 if(e.button !== 0 && isSelected){
22336 view.focusRow(rowIndex);
22337 }else if(e.ctrlKey && isSelected){
22338 this.deselectRow(rowIndex);
22339 }else if(!isSelected){
22340 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22341 view.focusRow(rowIndex);
22344 this.fireEvent("afterselectionchange", this);
22347 handleDragableRowClick : function(grid, rowIndex, e)
22349 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22350 this.selectRow(rowIndex, false);
22351 grid.view.focusRow(rowIndex);
22352 this.fireEvent("afterselectionchange", this);
22357 * Selects multiple rows.
22358 * @param {Array} rows Array of the indexes of the row to select
22359 * @param {Boolean} keepExisting (optional) True to keep existing selections
22361 selectRows : function(rows, keepExisting){
22363 this.clearSelections();
22365 for(var i = 0, len = rows.length; i < len; i++){
22366 this.selectRow(rows[i], true);
22371 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22372 * @param {Number} startRow The index of the first row in the range
22373 * @param {Number} endRow The index of the last row in the range
22374 * @param {Boolean} keepExisting (optional) True to retain existing selections
22376 selectRange : function(startRow, endRow, keepExisting){
22381 this.clearSelections();
22383 if(startRow <= endRow){
22384 for(var i = startRow; i <= endRow; i++){
22385 this.selectRow(i, true);
22388 for(var i = startRow; i >= endRow; i--){
22389 this.selectRow(i, true);
22395 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22396 * @param {Number} startRow The index of the first row in the range
22397 * @param {Number} endRow The index of the last row in the range
22399 deselectRange : function(startRow, endRow, preventViewNotify){
22403 for(var i = startRow; i <= endRow; i++){
22404 this.deselectRow(i, preventViewNotify);
22410 * @param {Number} row The index of the row to select
22411 * @param {Boolean} keepExisting (optional) True to keep existing selections
22413 selectRow : function(index, keepExisting, preventViewNotify){
22414 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22417 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22418 if(!keepExisting || this.singleSelect){
22419 this.clearSelections();
22421 var r = this.grid.dataSource.getAt(index);
22422 this.selections.add(r);
22423 this.last = this.lastActive = index;
22424 if(!preventViewNotify){
22425 this.grid.getView().onRowSelect(index);
22427 this.fireEvent("rowselect", this, index, r);
22428 this.fireEvent("selectionchange", this);
22434 * @param {Number} row The index of the row to deselect
22436 deselectRow : function(index, preventViewNotify){
22440 if(this.last == index){
22443 if(this.lastActive == index){
22444 this.lastActive = false;
22446 var r = this.grid.dataSource.getAt(index);
22447 this.selections.remove(r);
22448 if(!preventViewNotify){
22449 this.grid.getView().onRowDeselect(index);
22451 this.fireEvent("rowdeselect", this, index);
22452 this.fireEvent("selectionchange", this);
22456 restoreLast : function(){
22458 this.last = this._last;
22463 acceptsNav : function(row, col, cm){
22464 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22468 onEditorKey : function(field, e){
22469 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22474 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22476 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22478 }else if(k == e.ENTER && !e.ctrlKey){
22482 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22484 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22486 }else if(k == e.ESC){
22490 g.startEditing(newCell[0], newCell[1]);
22495 * Ext JS Library 1.1.1
22496 * Copyright(c) 2006-2007, Ext JS, LLC.
22498 * Originally Released Under LGPL - original licence link has changed is not relivant.
22501 * <script type="text/javascript">
22505 * @class Roo.bootstrap.PagingToolbar
22506 * @extends Roo.bootstrap.NavSimplebar
22507 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22509 * Create a new PagingToolbar
22510 * @param {Object} config The config object
22511 * @param {Roo.data.Store} store
22513 Roo.bootstrap.PagingToolbar = function(config)
22515 // old args format still supported... - xtype is prefered..
22516 // created from xtype...
22518 this.ds = config.dataSource;
22520 if (config.store && !this.ds) {
22521 this.store= Roo.factory(config.store, Roo.data);
22522 this.ds = this.store;
22523 this.ds.xmodule = this.xmodule || false;
22526 this.toolbarItems = [];
22527 if (config.items) {
22528 this.toolbarItems = config.items;
22531 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22536 this.bind(this.ds);
22539 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22543 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22545 * @cfg {Roo.data.Store} dataSource
22546 * The underlying data store providing the paged data
22549 * @cfg {String/HTMLElement/Element} container
22550 * container The id or element that will contain the toolbar
22553 * @cfg {Boolean} displayInfo
22554 * True to display the displayMsg (defaults to false)
22557 * @cfg {Number} pageSize
22558 * The number of records to display per page (defaults to 20)
22562 * @cfg {String} displayMsg
22563 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22565 displayMsg : 'Displaying {0} - {1} of {2}',
22567 * @cfg {String} emptyMsg
22568 * The message to display when no records are found (defaults to "No data to display")
22570 emptyMsg : 'No data to display',
22572 * Customizable piece of the default paging text (defaults to "Page")
22575 beforePageText : "Page",
22577 * Customizable piece of the default paging text (defaults to "of %0")
22580 afterPageText : "of {0}",
22582 * Customizable piece of the default paging text (defaults to "First Page")
22585 firstText : "First Page",
22587 * Customizable piece of the default paging text (defaults to "Previous Page")
22590 prevText : "Previous Page",
22592 * Customizable piece of the default paging text (defaults to "Next Page")
22595 nextText : "Next Page",
22597 * Customizable piece of the default paging text (defaults to "Last Page")
22600 lastText : "Last Page",
22602 * Customizable piece of the default paging text (defaults to "Refresh")
22605 refreshText : "Refresh",
22609 onRender : function(ct, position)
22611 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22612 this.navgroup.parentId = this.id;
22613 this.navgroup.onRender(this.el, null);
22614 // add the buttons to the navgroup
22616 if(this.displayInfo){
22617 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22618 this.displayEl = this.el.select('.x-paging-info', true).first();
22619 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22620 // this.displayEl = navel.el.select('span',true).first();
22626 Roo.each(_this.buttons, function(e){ // this might need to use render????
22627 Roo.factory(e).onRender(_this.el, null);
22631 Roo.each(_this.toolbarItems, function(e) {
22632 _this.navgroup.addItem(e);
22636 this.first = this.navgroup.addItem({
22637 tooltip: this.firstText,
22639 icon : 'fa fa-backward',
22641 preventDefault: true,
22642 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22645 this.prev = this.navgroup.addItem({
22646 tooltip: this.prevText,
22648 icon : 'fa fa-step-backward',
22650 preventDefault: true,
22651 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22653 //this.addSeparator();
22656 var field = this.navgroup.addItem( {
22658 cls : 'x-paging-position',
22660 html : this.beforePageText +
22661 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22662 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22665 this.field = field.el.select('input', true).first();
22666 this.field.on("keydown", this.onPagingKeydown, this);
22667 this.field.on("focus", function(){this.dom.select();});
22670 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22671 //this.field.setHeight(18);
22672 //this.addSeparator();
22673 this.next = this.navgroup.addItem({
22674 tooltip: this.nextText,
22676 html : ' <i class="fa fa-step-forward">',
22678 preventDefault: true,
22679 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22681 this.last = this.navgroup.addItem({
22682 tooltip: this.lastText,
22683 icon : 'fa fa-forward',
22686 preventDefault: true,
22687 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22689 //this.addSeparator();
22690 this.loading = this.navgroup.addItem({
22691 tooltip: this.refreshText,
22692 icon: 'fa fa-refresh',
22693 preventDefault: true,
22694 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22700 updateInfo : function(){
22701 if(this.displayEl){
22702 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22703 var msg = count == 0 ?
22707 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22709 this.displayEl.update(msg);
22714 onLoad : function(ds, r, o){
22715 this.cursor = o.params ? o.params.start : 0;
22716 var d = this.getPageData(),
22720 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22721 this.field.dom.value = ap;
22722 this.first.setDisabled(ap == 1);
22723 this.prev.setDisabled(ap == 1);
22724 this.next.setDisabled(ap == ps);
22725 this.last.setDisabled(ap == ps);
22726 this.loading.enable();
22731 getPageData : function(){
22732 var total = this.ds.getTotalCount();
22735 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22736 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22741 onLoadError : function(){
22742 this.loading.enable();
22746 onPagingKeydown : function(e){
22747 var k = e.getKey();
22748 var d = this.getPageData();
22750 var v = this.field.dom.value, pageNum;
22751 if(!v || isNaN(pageNum = parseInt(v, 10))){
22752 this.field.dom.value = d.activePage;
22755 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22756 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22759 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))
22761 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22762 this.field.dom.value = pageNum;
22763 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22766 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22768 var v = this.field.dom.value, pageNum;
22769 var increment = (e.shiftKey) ? 10 : 1;
22770 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22773 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22774 this.field.dom.value = d.activePage;
22777 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22779 this.field.dom.value = parseInt(v, 10) + increment;
22780 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22781 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22788 beforeLoad : function(){
22790 this.loading.disable();
22795 onClick : function(which){
22804 ds.load({params:{start: 0, limit: this.pageSize}});
22807 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22810 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22813 var total = ds.getTotalCount();
22814 var extra = total % this.pageSize;
22815 var lastStart = extra ? (total - extra) : total-this.pageSize;
22816 ds.load({params:{start: lastStart, limit: this.pageSize}});
22819 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22825 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22826 * @param {Roo.data.Store} store The data store to unbind
22828 unbind : function(ds){
22829 ds.un("beforeload", this.beforeLoad, this);
22830 ds.un("load", this.onLoad, this);
22831 ds.un("loadexception", this.onLoadError, this);
22832 ds.un("remove", this.updateInfo, this);
22833 ds.un("add", this.updateInfo, this);
22834 this.ds = undefined;
22838 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22839 * @param {Roo.data.Store} store The data store to bind
22841 bind : function(ds){
22842 ds.on("beforeload", this.beforeLoad, this);
22843 ds.on("load", this.onLoad, this);
22844 ds.on("loadexception", this.onLoadError, this);
22845 ds.on("remove", this.updateInfo, this);
22846 ds.on("add", this.updateInfo, this);
22857 * @class Roo.bootstrap.MessageBar
22858 * @extends Roo.bootstrap.Component
22859 * Bootstrap MessageBar class
22860 * @cfg {String} html contents of the MessageBar
22861 * @cfg {String} weight (info | success | warning | danger) default info
22862 * @cfg {String} beforeClass insert the bar before the given class
22863 * @cfg {Boolean} closable (true | false) default false
22864 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22867 * Create a new Element
22868 * @param {Object} config The config object
22871 Roo.bootstrap.MessageBar = function(config){
22872 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22875 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22881 beforeClass: 'bootstrap-sticky-wrap',
22883 getAutoCreate : function(){
22887 cls: 'alert alert-dismissable alert-' + this.weight,
22892 html: this.html || ''
22898 cfg.cls += ' alert-messages-fixed';
22912 onRender : function(ct, position)
22914 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22917 var cfg = Roo.apply({}, this.getAutoCreate());
22921 cfg.cls += ' ' + this.cls;
22924 cfg.style = this.style;
22926 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22928 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22931 this.el.select('>button.close').on('click', this.hide, this);
22937 if (!this.rendered) {
22943 this.fireEvent('show', this);
22949 if (!this.rendered) {
22955 this.fireEvent('hide', this);
22958 update : function()
22960 // var e = this.el.dom.firstChild;
22962 // if(this.closable){
22963 // e = e.nextSibling;
22966 // e.data = this.html || '';
22968 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22984 * @class Roo.bootstrap.Graph
22985 * @extends Roo.bootstrap.Component
22986 * Bootstrap Graph class
22990 @cfg {String} graphtype bar | vbar | pie
22991 @cfg {number} g_x coodinator | centre x (pie)
22992 @cfg {number} g_y coodinator | centre y (pie)
22993 @cfg {number} g_r radius (pie)
22994 @cfg {number} g_height height of the chart (respected by all elements in the set)
22995 @cfg {number} g_width width of the chart (respected by all elements in the set)
22996 @cfg {Object} title The title of the chart
22999 -opts (object) options for the chart
23001 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23002 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23004 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.
23005 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23007 o stretch (boolean)
23009 -opts (object) options for the pie
23012 o startAngle (number)
23013 o endAngle (number)
23017 * Create a new Input
23018 * @param {Object} config The config object
23021 Roo.bootstrap.Graph = function(config){
23022 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23028 * The img click event for the img.
23029 * @param {Roo.EventObject} e
23035 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23046 //g_colors: this.colors,
23053 getAutoCreate : function(){
23064 onRender : function(ct,position){
23067 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23069 if (typeof(Raphael) == 'undefined') {
23070 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23074 this.raphael = Raphael(this.el.dom);
23076 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23077 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23078 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23079 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23081 r.text(160, 10, "Single Series Chart").attr(txtattr);
23082 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23083 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23084 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23086 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23087 r.barchart(330, 10, 300, 220, data1);
23088 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23089 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23092 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23093 // r.barchart(30, 30, 560, 250, xdata, {
23094 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23095 // axis : "0 0 1 1",
23096 // axisxlabels : xdata
23097 // //yvalues : cols,
23100 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23102 // this.load(null,xdata,{
23103 // axis : "0 0 1 1",
23104 // axisxlabels : xdata
23109 load : function(graphtype,xdata,opts)
23111 this.raphael.clear();
23113 graphtype = this.graphtype;
23118 var r = this.raphael,
23119 fin = function () {
23120 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23122 fout = function () {
23123 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23125 pfin = function() {
23126 this.sector.stop();
23127 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23130 this.label[0].stop();
23131 this.label[0].attr({ r: 7.5 });
23132 this.label[1].attr({ "font-weight": 800 });
23135 pfout = function() {
23136 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23139 this.label[0].animate({ r: 5 }, 500, "bounce");
23140 this.label[1].attr({ "font-weight": 400 });
23146 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23149 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23152 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23153 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23155 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23162 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23167 setTitle: function(o)
23172 initEvents: function() {
23175 this.el.on('click', this.onClick, this);
23179 onClick : function(e)
23181 Roo.log('img onclick');
23182 this.fireEvent('click', this, e);
23194 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23197 * @class Roo.bootstrap.dash.NumberBox
23198 * @extends Roo.bootstrap.Component
23199 * Bootstrap NumberBox class
23200 * @cfg {String} headline Box headline
23201 * @cfg {String} content Box content
23202 * @cfg {String} icon Box icon
23203 * @cfg {String} footer Footer text
23204 * @cfg {String} fhref Footer href
23207 * Create a new NumberBox
23208 * @param {Object} config The config object
23212 Roo.bootstrap.dash.NumberBox = function(config){
23213 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23217 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23226 getAutoCreate : function(){
23230 cls : 'small-box ',
23238 cls : 'roo-headline',
23239 html : this.headline
23243 cls : 'roo-content',
23244 html : this.content
23258 cls : 'ion ' + this.icon
23267 cls : 'small-box-footer',
23268 href : this.fhref || '#',
23272 cfg.cn.push(footer);
23279 onRender : function(ct,position){
23280 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23287 setHeadline: function (value)
23289 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23292 setFooter: function (value, href)
23294 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23297 this.el.select('a.small-box-footer',true).first().attr('href', href);
23302 setContent: function (value)
23304 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23307 initEvents: function()
23321 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23324 * @class Roo.bootstrap.dash.TabBox
23325 * @extends Roo.bootstrap.Component
23326 * Bootstrap TabBox class
23327 * @cfg {String} title Title of the TabBox
23328 * @cfg {String} icon Icon of the TabBox
23329 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23330 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23333 * Create a new TabBox
23334 * @param {Object} config The config object
23338 Roo.bootstrap.dash.TabBox = function(config){
23339 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23344 * When a pane is added
23345 * @param {Roo.bootstrap.dash.TabPane} pane
23349 * @event activatepane
23350 * When a pane is activated
23351 * @param {Roo.bootstrap.dash.TabPane} pane
23353 "activatepane" : true
23361 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23366 tabScrollable : false,
23368 getChildContainer : function()
23370 return this.el.select('.tab-content', true).first();
23373 getAutoCreate : function(){
23377 cls: 'pull-left header',
23385 cls: 'fa ' + this.icon
23391 cls: 'nav nav-tabs pull-right',
23397 if(this.tabScrollable){
23404 cls: 'nav nav-tabs pull-right',
23415 cls: 'nav-tabs-custom',
23420 cls: 'tab-content no-padding',
23428 initEvents : function()
23430 //Roo.log('add add pane handler');
23431 this.on('addpane', this.onAddPane, this);
23434 * Updates the box title
23435 * @param {String} html to set the title to.
23437 setTitle : function(value)
23439 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23441 onAddPane : function(pane)
23443 this.panes.push(pane);
23444 //Roo.log('addpane');
23446 // tabs are rendere left to right..
23447 if(!this.showtabs){
23451 var ctr = this.el.select('.nav-tabs', true).first();
23454 var existing = ctr.select('.nav-tab',true);
23455 var qty = existing.getCount();;
23458 var tab = ctr.createChild({
23460 cls : 'nav-tab' + (qty ? '' : ' active'),
23468 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23471 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23473 pane.el.addClass('active');
23478 onTabClick : function(ev,un,ob,pane)
23480 //Roo.log('tab - prev default');
23481 ev.preventDefault();
23484 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23485 pane.tab.addClass('active');
23486 //Roo.log(pane.title);
23487 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23488 // technically we should have a deactivate event.. but maybe add later.
23489 // and it should not de-activate the selected tab...
23490 this.fireEvent('activatepane', pane);
23491 pane.el.addClass('active');
23492 pane.fireEvent('activate');
23497 getActivePane : function()
23500 Roo.each(this.panes, function(p) {
23501 if(p.el.hasClass('active')){
23522 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23524 * @class Roo.bootstrap.TabPane
23525 * @extends Roo.bootstrap.Component
23526 * Bootstrap TabPane class
23527 * @cfg {Boolean} active (false | true) Default false
23528 * @cfg {String} title title of panel
23532 * Create a new TabPane
23533 * @param {Object} config The config object
23536 Roo.bootstrap.dash.TabPane = function(config){
23537 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23543 * When a pane is activated
23544 * @param {Roo.bootstrap.dash.TabPane} pane
23551 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23556 // the tabBox that this is attached to.
23559 getAutoCreate : function()
23567 cfg.cls += ' active';
23572 initEvents : function()
23574 //Roo.log('trigger add pane handler');
23575 this.parent().fireEvent('addpane', this)
23579 * Updates the tab title
23580 * @param {String} html to set the title to.
23582 setTitle: function(str)
23588 this.tab.select('a', true).first().dom.innerHTML = str;
23605 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23608 * @class Roo.bootstrap.menu.Menu
23609 * @extends Roo.bootstrap.Component
23610 * Bootstrap Menu class - container for Menu
23611 * @cfg {String} html Text of the menu
23612 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23613 * @cfg {String} icon Font awesome icon
23614 * @cfg {String} pos Menu align to (top | bottom) default bottom
23618 * Create a new Menu
23619 * @param {Object} config The config object
23623 Roo.bootstrap.menu.Menu = function(config){
23624 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23628 * @event beforeshow
23629 * Fires before this menu is displayed
23630 * @param {Roo.bootstrap.menu.Menu} this
23634 * @event beforehide
23635 * Fires before this menu is hidden
23636 * @param {Roo.bootstrap.menu.Menu} this
23641 * Fires after this menu is displayed
23642 * @param {Roo.bootstrap.menu.Menu} this
23647 * Fires after this menu is hidden
23648 * @param {Roo.bootstrap.menu.Menu} this
23653 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23654 * @param {Roo.bootstrap.menu.Menu} this
23655 * @param {Roo.EventObject} e
23662 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23666 weight : 'default',
23671 getChildContainer : function() {
23672 if(this.isSubMenu){
23676 return this.el.select('ul.dropdown-menu', true).first();
23679 getAutoCreate : function()
23684 cls : 'roo-menu-text',
23692 cls : 'fa ' + this.icon
23703 cls : 'dropdown-button btn btn-' + this.weight,
23708 cls : 'dropdown-toggle btn btn-' + this.weight,
23718 cls : 'dropdown-menu'
23724 if(this.pos == 'top'){
23725 cfg.cls += ' dropup';
23728 if(this.isSubMenu){
23731 cls : 'dropdown-menu'
23738 onRender : function(ct, position)
23740 this.isSubMenu = ct.hasClass('dropdown-submenu');
23742 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23745 initEvents : function()
23747 if(this.isSubMenu){
23751 this.hidden = true;
23753 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23754 this.triggerEl.on('click', this.onTriggerPress, this);
23756 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23757 this.buttonEl.on('click', this.onClick, this);
23763 if(this.isSubMenu){
23767 return this.el.select('ul.dropdown-menu', true).first();
23770 onClick : function(e)
23772 this.fireEvent("click", this, e);
23775 onTriggerPress : function(e)
23777 if (this.isVisible()) {
23784 isVisible : function(){
23785 return !this.hidden;
23790 this.fireEvent("beforeshow", this);
23792 this.hidden = false;
23793 this.el.addClass('open');
23795 Roo.get(document).on("mouseup", this.onMouseUp, this);
23797 this.fireEvent("show", this);
23804 this.fireEvent("beforehide", this);
23806 this.hidden = true;
23807 this.el.removeClass('open');
23809 Roo.get(document).un("mouseup", this.onMouseUp);
23811 this.fireEvent("hide", this);
23814 onMouseUp : function()
23828 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23831 * @class Roo.bootstrap.menu.Item
23832 * @extends Roo.bootstrap.Component
23833 * Bootstrap MenuItem class
23834 * @cfg {Boolean} submenu (true | false) default false
23835 * @cfg {String} html text of the item
23836 * @cfg {String} href the link
23837 * @cfg {Boolean} disable (true | false) default false
23838 * @cfg {Boolean} preventDefault (true | false) default true
23839 * @cfg {String} icon Font awesome icon
23840 * @cfg {String} pos Submenu align to (left | right) default right
23844 * Create a new Item
23845 * @param {Object} config The config object
23849 Roo.bootstrap.menu.Item = function(config){
23850 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23854 * Fires when the mouse is hovering over this menu
23855 * @param {Roo.bootstrap.menu.Item} this
23856 * @param {Roo.EventObject} e
23861 * Fires when the mouse exits this menu
23862 * @param {Roo.bootstrap.menu.Item} this
23863 * @param {Roo.EventObject} e
23869 * The raw click event for the entire grid.
23870 * @param {Roo.EventObject} e
23876 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23881 preventDefault: true,
23886 getAutoCreate : function()
23891 cls : 'roo-menu-item-text',
23899 cls : 'fa ' + this.icon
23908 href : this.href || '#',
23915 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23919 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23921 if(this.pos == 'left'){
23922 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23929 initEvents : function()
23931 this.el.on('mouseover', this.onMouseOver, this);
23932 this.el.on('mouseout', this.onMouseOut, this);
23934 this.el.select('a', true).first().on('click', this.onClick, this);
23938 onClick : function(e)
23940 if(this.preventDefault){
23941 e.preventDefault();
23944 this.fireEvent("click", this, e);
23947 onMouseOver : function(e)
23949 if(this.submenu && this.pos == 'left'){
23950 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23953 this.fireEvent("mouseover", this, e);
23956 onMouseOut : function(e)
23958 this.fireEvent("mouseout", this, e);
23970 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23973 * @class Roo.bootstrap.menu.Separator
23974 * @extends Roo.bootstrap.Component
23975 * Bootstrap Separator class
23978 * Create a new Separator
23979 * @param {Object} config The config object
23983 Roo.bootstrap.menu.Separator = function(config){
23984 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23987 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23989 getAutoCreate : function(){
24010 * @class Roo.bootstrap.Tooltip
24011 * Bootstrap Tooltip class
24012 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24013 * to determine which dom element triggers the tooltip.
24015 * It needs to add support for additional attributes like tooltip-position
24018 * Create a new Toolti
24019 * @param {Object} config The config object
24022 Roo.bootstrap.Tooltip = function(config){
24023 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24026 Roo.apply(Roo.bootstrap.Tooltip, {
24028 * @function init initialize tooltip monitoring.
24032 currentTip : false,
24033 currentRegion : false,
24039 Roo.get(document).on('mouseover', this.enter ,this);
24040 Roo.get(document).on('mouseout', this.leave, this);
24043 this.currentTip = new Roo.bootstrap.Tooltip();
24046 enter : function(ev)
24048 var dom = ev.getTarget();
24050 //Roo.log(['enter',dom]);
24051 var el = Roo.fly(dom);
24052 if (this.currentEl) {
24054 //Roo.log(this.currentEl);
24055 //Roo.log(this.currentEl.contains(dom));
24056 if (this.currentEl == el) {
24059 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24065 if (this.currentTip.el) {
24066 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24071 // you can not look for children, as if el is the body.. then everythign is the child..
24072 if (!el.attr('tooltip')) { //
24073 if (!el.select("[tooltip]").elements.length) {
24076 // is the mouse over this child...?
24077 bindEl = el.select("[tooltip]").first();
24078 var xy = ev.getXY();
24079 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24080 //Roo.log("not in region.");
24083 //Roo.log("child element over..");
24086 this.currentEl = bindEl;
24087 this.currentTip.bind(bindEl);
24088 this.currentRegion = Roo.lib.Region.getRegion(dom);
24089 this.currentTip.enter();
24092 leave : function(ev)
24094 var dom = ev.getTarget();
24095 //Roo.log(['leave',dom]);
24096 if (!this.currentEl) {
24101 if (dom != this.currentEl.dom) {
24104 var xy = ev.getXY();
24105 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24108 // only activate leave if mouse cursor is outside... bounding box..
24113 if (this.currentTip) {
24114 this.currentTip.leave();
24116 //Roo.log('clear currentEl');
24117 this.currentEl = false;
24122 'left' : ['r-l', [-2,0], 'right'],
24123 'right' : ['l-r', [2,0], 'left'],
24124 'bottom' : ['t-b', [0,2], 'top'],
24125 'top' : [ 'b-t', [0,-2], 'bottom']
24131 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24136 delay : null, // can be { show : 300 , hide: 500}
24140 hoverState : null, //???
24142 placement : 'bottom',
24144 getAutoCreate : function(){
24151 cls : 'tooltip-arrow'
24154 cls : 'tooltip-inner'
24161 bind : function(el)
24167 enter : function () {
24169 if (this.timeout != null) {
24170 clearTimeout(this.timeout);
24173 this.hoverState = 'in';
24174 //Roo.log("enter - show");
24175 if (!this.delay || !this.delay.show) {
24180 this.timeout = setTimeout(function () {
24181 if (_t.hoverState == 'in') {
24184 }, this.delay.show);
24188 clearTimeout(this.timeout);
24190 this.hoverState = 'out';
24191 if (!this.delay || !this.delay.hide) {
24197 this.timeout = setTimeout(function () {
24198 //Roo.log("leave - timeout");
24200 if (_t.hoverState == 'out') {
24202 Roo.bootstrap.Tooltip.currentEl = false;
24210 this.render(document.body);
24213 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24215 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24217 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24219 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24221 var placement = typeof this.placement == 'function' ?
24222 this.placement.call(this, this.el, on_el) :
24225 var autoToken = /\s?auto?\s?/i;
24226 var autoPlace = autoToken.test(placement);
24228 placement = placement.replace(autoToken, '') || 'top';
24232 //this.el.setXY([0,0]);
24234 //this.el.dom.style.display='block';
24236 //this.el.appendTo(on_el);
24238 var p = this.getPosition();
24239 var box = this.el.getBox();
24245 var align = Roo.bootstrap.Tooltip.alignment[placement];
24247 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24249 if(placement == 'top' || placement == 'bottom'){
24251 placement = 'right';
24254 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24255 placement = 'left';
24259 align = Roo.bootstrap.Tooltip.alignment[placement];
24261 this.el.alignTo(this.bindEl, align[0],align[1]);
24262 //var arrow = this.el.select('.arrow',true).first();
24263 //arrow.set(align[2],
24265 this.el.addClass(placement);
24267 this.el.addClass('in fade');
24269 this.hoverState = null;
24271 if (this.el.hasClass('fade')) {
24282 //this.el.setXY([0,0]);
24283 this.el.removeClass('in');
24299 * @class Roo.bootstrap.LocationPicker
24300 * @extends Roo.bootstrap.Component
24301 * Bootstrap LocationPicker class
24302 * @cfg {Number} latitude Position when init default 0
24303 * @cfg {Number} longitude Position when init default 0
24304 * @cfg {Number} zoom default 15
24305 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24306 * @cfg {Boolean} mapTypeControl default false
24307 * @cfg {Boolean} disableDoubleClickZoom default false
24308 * @cfg {Boolean} scrollwheel default true
24309 * @cfg {Boolean} streetViewControl default false
24310 * @cfg {Number} radius default 0
24311 * @cfg {String} locationName
24312 * @cfg {Boolean} draggable default true
24313 * @cfg {Boolean} enableAutocomplete default false
24314 * @cfg {Boolean} enableReverseGeocode default true
24315 * @cfg {String} markerTitle
24318 * Create a new LocationPicker
24319 * @param {Object} config The config object
24323 Roo.bootstrap.LocationPicker = function(config){
24325 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24330 * Fires when the picker initialized.
24331 * @param {Roo.bootstrap.LocationPicker} this
24332 * @param {Google Location} location
24336 * @event positionchanged
24337 * Fires when the picker position changed.
24338 * @param {Roo.bootstrap.LocationPicker} this
24339 * @param {Google Location} location
24341 positionchanged : true,
24344 * Fires when the map resize.
24345 * @param {Roo.bootstrap.LocationPicker} this
24350 * Fires when the map show.
24351 * @param {Roo.bootstrap.LocationPicker} this
24356 * Fires when the map hide.
24357 * @param {Roo.bootstrap.LocationPicker} this
24362 * Fires when click the map.
24363 * @param {Roo.bootstrap.LocationPicker} this
24364 * @param {Map event} e
24368 * @event mapRightClick
24369 * Fires when right click the map.
24370 * @param {Roo.bootstrap.LocationPicker} this
24371 * @param {Map event} e
24373 mapRightClick : true,
24375 * @event markerClick
24376 * Fires when click the marker.
24377 * @param {Roo.bootstrap.LocationPicker} this
24378 * @param {Map event} e
24380 markerClick : true,
24382 * @event markerRightClick
24383 * Fires when right click the marker.
24384 * @param {Roo.bootstrap.LocationPicker} this
24385 * @param {Map event} e
24387 markerRightClick : true,
24389 * @event OverlayViewDraw
24390 * Fires when OverlayView Draw
24391 * @param {Roo.bootstrap.LocationPicker} this
24393 OverlayViewDraw : true,
24395 * @event OverlayViewOnAdd
24396 * Fires when OverlayView Draw
24397 * @param {Roo.bootstrap.LocationPicker} this
24399 OverlayViewOnAdd : true,
24401 * @event OverlayViewOnRemove
24402 * Fires when OverlayView Draw
24403 * @param {Roo.bootstrap.LocationPicker} this
24405 OverlayViewOnRemove : true,
24407 * @event OverlayViewShow
24408 * Fires when OverlayView Draw
24409 * @param {Roo.bootstrap.LocationPicker} this
24410 * @param {Pixel} cpx
24412 OverlayViewShow : true,
24414 * @event OverlayViewHide
24415 * Fires when OverlayView Draw
24416 * @param {Roo.bootstrap.LocationPicker} this
24418 OverlayViewHide : true,
24420 * @event loadexception
24421 * Fires when load google lib failed.
24422 * @param {Roo.bootstrap.LocationPicker} this
24424 loadexception : true
24429 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24431 gMapContext: false,
24437 mapTypeControl: false,
24438 disableDoubleClickZoom: false,
24440 streetViewControl: false,
24444 enableAutocomplete: false,
24445 enableReverseGeocode: true,
24448 getAutoCreate: function()
24453 cls: 'roo-location-picker'
24459 initEvents: function(ct, position)
24461 if(!this.el.getWidth() || this.isApplied()){
24465 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24470 initial: function()
24472 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24473 this.fireEvent('loadexception', this);
24477 if(!this.mapTypeId){
24478 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24481 this.gMapContext = this.GMapContext();
24483 this.initOverlayView();
24485 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24489 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24490 _this.setPosition(_this.gMapContext.marker.position);
24493 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24494 _this.fireEvent('mapClick', this, event);
24498 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24499 _this.fireEvent('mapRightClick', this, event);
24503 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24504 _this.fireEvent('markerClick', this, event);
24508 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24509 _this.fireEvent('markerRightClick', this, event);
24513 this.setPosition(this.gMapContext.location);
24515 this.fireEvent('initial', this, this.gMapContext.location);
24518 initOverlayView: function()
24522 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24526 _this.fireEvent('OverlayViewDraw', _this);
24531 _this.fireEvent('OverlayViewOnAdd', _this);
24534 onRemove: function()
24536 _this.fireEvent('OverlayViewOnRemove', _this);
24539 show: function(cpx)
24541 _this.fireEvent('OverlayViewShow', _this, cpx);
24546 _this.fireEvent('OverlayViewHide', _this);
24552 fromLatLngToContainerPixel: function(event)
24554 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24557 isApplied: function()
24559 return this.getGmapContext() == false ? false : true;
24562 getGmapContext: function()
24564 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24567 GMapContext: function()
24569 var position = new google.maps.LatLng(this.latitude, this.longitude);
24571 var _map = new google.maps.Map(this.el.dom, {
24574 mapTypeId: this.mapTypeId,
24575 mapTypeControl: this.mapTypeControl,
24576 disableDoubleClickZoom: this.disableDoubleClickZoom,
24577 scrollwheel: this.scrollwheel,
24578 streetViewControl: this.streetViewControl,
24579 locationName: this.locationName,
24580 draggable: this.draggable,
24581 enableAutocomplete: this.enableAutocomplete,
24582 enableReverseGeocode: this.enableReverseGeocode
24585 var _marker = new google.maps.Marker({
24586 position: position,
24588 title: this.markerTitle,
24589 draggable: this.draggable
24596 location: position,
24597 radius: this.radius,
24598 locationName: this.locationName,
24599 addressComponents: {
24600 formatted_address: null,
24601 addressLine1: null,
24602 addressLine2: null,
24604 streetNumber: null,
24608 stateOrProvince: null
24611 domContainer: this.el.dom,
24612 geodecoder: new google.maps.Geocoder()
24616 drawCircle: function(center, radius, options)
24618 if (this.gMapContext.circle != null) {
24619 this.gMapContext.circle.setMap(null);
24623 options = Roo.apply({}, options, {
24624 strokeColor: "#0000FF",
24625 strokeOpacity: .35,
24627 fillColor: "#0000FF",
24631 options.map = this.gMapContext.map;
24632 options.radius = radius;
24633 options.center = center;
24634 this.gMapContext.circle = new google.maps.Circle(options);
24635 return this.gMapContext.circle;
24641 setPosition: function(location)
24643 this.gMapContext.location = location;
24644 this.gMapContext.marker.setPosition(location);
24645 this.gMapContext.map.panTo(location);
24646 this.drawCircle(location, this.gMapContext.radius, {});
24650 if (this.gMapContext.settings.enableReverseGeocode) {
24651 this.gMapContext.geodecoder.geocode({
24652 latLng: this.gMapContext.location
24653 }, function(results, status) {
24655 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24656 _this.gMapContext.locationName = results[0].formatted_address;
24657 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24659 _this.fireEvent('positionchanged', this, location);
24666 this.fireEvent('positionchanged', this, location);
24671 google.maps.event.trigger(this.gMapContext.map, "resize");
24673 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24675 this.fireEvent('resize', this);
24678 setPositionByLatLng: function(latitude, longitude)
24680 this.setPosition(new google.maps.LatLng(latitude, longitude));
24683 getCurrentPosition: function()
24686 latitude: this.gMapContext.location.lat(),
24687 longitude: this.gMapContext.location.lng()
24691 getAddressName: function()
24693 return this.gMapContext.locationName;
24696 getAddressComponents: function()
24698 return this.gMapContext.addressComponents;
24701 address_component_from_google_geocode: function(address_components)
24705 for (var i = 0; i < address_components.length; i++) {
24706 var component = address_components[i];
24707 if (component.types.indexOf("postal_code") >= 0) {
24708 result.postalCode = component.short_name;
24709 } else if (component.types.indexOf("street_number") >= 0) {
24710 result.streetNumber = component.short_name;
24711 } else if (component.types.indexOf("route") >= 0) {
24712 result.streetName = component.short_name;
24713 } else if (component.types.indexOf("neighborhood") >= 0) {
24714 result.city = component.short_name;
24715 } else if (component.types.indexOf("locality") >= 0) {
24716 result.city = component.short_name;
24717 } else if (component.types.indexOf("sublocality") >= 0) {
24718 result.district = component.short_name;
24719 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24720 result.stateOrProvince = component.short_name;
24721 } else if (component.types.indexOf("country") >= 0) {
24722 result.country = component.short_name;
24726 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24727 result.addressLine2 = "";
24731 setZoomLevel: function(zoom)
24733 this.gMapContext.map.setZoom(zoom);
24746 this.fireEvent('show', this);
24757 this.fireEvent('hide', this);
24762 Roo.apply(Roo.bootstrap.LocationPicker, {
24764 OverlayView : function(map, options)
24766 options = options || {};
24780 * @class Roo.bootstrap.Alert
24781 * @extends Roo.bootstrap.Component
24782 * Bootstrap Alert class
24783 * @cfg {String} title The title of alert
24784 * @cfg {String} html The content of alert
24785 * @cfg {String} weight ( success | info | warning | danger )
24786 * @cfg {String} faicon font-awesomeicon
24789 * Create a new alert
24790 * @param {Object} config The config object
24794 Roo.bootstrap.Alert = function(config){
24795 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24799 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24806 getAutoCreate : function()
24815 cls : 'roo-alert-icon'
24820 cls : 'roo-alert-title',
24825 cls : 'roo-alert-text',
24832 cfg.cn[0].cls += ' fa ' + this.faicon;
24836 cfg.cls += ' alert-' + this.weight;
24842 initEvents: function()
24844 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24847 setTitle : function(str)
24849 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24852 setText : function(str)
24854 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24857 setWeight : function(weight)
24860 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24863 this.weight = weight;
24865 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24868 setIcon : function(icon)
24871 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24874 this.faicon = icon;
24876 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24897 * @class Roo.bootstrap.UploadCropbox
24898 * @extends Roo.bootstrap.Component
24899 * Bootstrap UploadCropbox class
24900 * @cfg {String} emptyText show when image has been loaded
24901 * @cfg {String} rotateNotify show when image too small to rotate
24902 * @cfg {Number} errorTimeout default 3000
24903 * @cfg {Number} minWidth default 300
24904 * @cfg {Number} minHeight default 300
24905 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24906 * @cfg {Boolean} isDocument (true|false) default false
24907 * @cfg {String} url action url
24908 * @cfg {String} paramName default 'imageUpload'
24909 * @cfg {String} method default POST
24910 * @cfg {Boolean} loadMask (true|false) default true
24911 * @cfg {Boolean} loadingText default 'Loading...'
24914 * Create a new UploadCropbox
24915 * @param {Object} config The config object
24918 Roo.bootstrap.UploadCropbox = function(config){
24919 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24923 * @event beforeselectfile
24924 * Fire before select file
24925 * @param {Roo.bootstrap.UploadCropbox} this
24927 "beforeselectfile" : true,
24930 * Fire after initEvent
24931 * @param {Roo.bootstrap.UploadCropbox} this
24936 * Fire after initEvent
24937 * @param {Roo.bootstrap.UploadCropbox} this
24938 * @param {String} data
24943 * Fire when preparing the file data
24944 * @param {Roo.bootstrap.UploadCropbox} this
24945 * @param {Object} file
24950 * Fire when get exception
24951 * @param {Roo.bootstrap.UploadCropbox} this
24952 * @param {XMLHttpRequest} xhr
24954 "exception" : true,
24956 * @event beforeloadcanvas
24957 * Fire before load the canvas
24958 * @param {Roo.bootstrap.UploadCropbox} this
24959 * @param {String} src
24961 "beforeloadcanvas" : true,
24964 * Fire when trash image
24965 * @param {Roo.bootstrap.UploadCropbox} this
24970 * Fire when download the image
24971 * @param {Roo.bootstrap.UploadCropbox} this
24975 * @event footerbuttonclick
24976 * Fire when footerbuttonclick
24977 * @param {Roo.bootstrap.UploadCropbox} this
24978 * @param {String} type
24980 "footerbuttonclick" : true,
24984 * @param {Roo.bootstrap.UploadCropbox} this
24989 * Fire when rotate the image
24990 * @param {Roo.bootstrap.UploadCropbox} this
24991 * @param {String} pos
24996 * Fire when inspect the file
24997 * @param {Roo.bootstrap.UploadCropbox} this
24998 * @param {Object} file
25003 * Fire when xhr upload the file
25004 * @param {Roo.bootstrap.UploadCropbox} this
25005 * @param {Object} data
25010 * Fire when arrange the file data
25011 * @param {Roo.bootstrap.UploadCropbox} this
25012 * @param {Object} formData
25017 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25020 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25022 emptyText : 'Click to upload image',
25023 rotateNotify : 'Image is too small to rotate',
25024 errorTimeout : 3000,
25038 cropType : 'image/jpeg',
25040 canvasLoaded : false,
25041 isDocument : false,
25043 paramName : 'imageUpload',
25045 loadingText : 'Loading...',
25048 getAutoCreate : function()
25052 cls : 'roo-upload-cropbox',
25056 cls : 'roo-upload-cropbox-selector',
25061 cls : 'roo-upload-cropbox-body',
25062 style : 'cursor:pointer',
25066 cls : 'roo-upload-cropbox-preview'
25070 cls : 'roo-upload-cropbox-thumb'
25074 cls : 'roo-upload-cropbox-empty-notify',
25075 html : this.emptyText
25079 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25080 html : this.rotateNotify
25086 cls : 'roo-upload-cropbox-footer',
25089 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25099 onRender : function(ct, position)
25101 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25103 if (this.buttons.length) {
25105 Roo.each(this.buttons, function(bb) {
25107 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25109 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25115 this.maskEl = this.el;
25119 initEvents : function()
25121 this.urlAPI = (window.createObjectURL && window) ||
25122 (window.URL && URL.revokeObjectURL && URL) ||
25123 (window.webkitURL && webkitURL);
25125 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25126 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25128 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25129 this.selectorEl.hide();
25131 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25132 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25134 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25135 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25136 this.thumbEl.hide();
25138 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25139 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25141 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25142 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25143 this.errorEl.hide();
25145 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25146 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25147 this.footerEl.hide();
25149 this.setThumbBoxSize();
25155 this.fireEvent('initial', this);
25162 window.addEventListener("resize", function() { _this.resize(); } );
25164 this.bodyEl.on('click', this.beforeSelectFile, this);
25167 this.bodyEl.on('touchstart', this.onTouchStart, this);
25168 this.bodyEl.on('touchmove', this.onTouchMove, this);
25169 this.bodyEl.on('touchend', this.onTouchEnd, this);
25173 this.bodyEl.on('mousedown', this.onMouseDown, this);
25174 this.bodyEl.on('mousemove', this.onMouseMove, this);
25175 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25176 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25177 Roo.get(document).on('mouseup', this.onMouseUp, this);
25180 this.selectorEl.on('change', this.onFileSelected, this);
25186 this.baseScale = 1;
25188 this.baseRotate = 1;
25189 this.dragable = false;
25190 this.pinching = false;
25193 this.cropData = false;
25194 this.notifyEl.dom.innerHTML = this.emptyText;
25196 this.selectorEl.dom.value = '';
25200 resize : function()
25202 if(this.fireEvent('resize', this) != false){
25203 this.setThumbBoxPosition();
25204 this.setCanvasPosition();
25208 onFooterButtonClick : function(e, el, o, type)
25211 case 'rotate-left' :
25212 this.onRotateLeft(e);
25214 case 'rotate-right' :
25215 this.onRotateRight(e);
25218 this.beforeSelectFile(e);
25233 this.fireEvent('footerbuttonclick', this, type);
25236 beforeSelectFile : function(e)
25238 e.preventDefault();
25240 if(this.fireEvent('beforeselectfile', this) != false){
25241 this.selectorEl.dom.click();
25245 onFileSelected : function(e)
25247 e.preventDefault();
25249 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25253 var file = this.selectorEl.dom.files[0];
25255 if(this.fireEvent('inspect', this, file) != false){
25256 this.prepare(file);
25261 trash : function(e)
25263 this.fireEvent('trash', this);
25266 download : function(e)
25268 this.fireEvent('download', this);
25271 loadCanvas : function(src)
25273 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25277 this.imageEl = document.createElement('img');
25281 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25283 this.imageEl.src = src;
25287 onLoadCanvas : function()
25289 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25290 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25292 this.bodyEl.un('click', this.beforeSelectFile, this);
25294 this.notifyEl.hide();
25295 this.thumbEl.show();
25296 this.footerEl.show();
25298 this.baseRotateLevel();
25300 if(this.isDocument){
25301 this.setThumbBoxSize();
25304 this.setThumbBoxPosition();
25306 this.baseScaleLevel();
25312 this.canvasLoaded = true;
25315 this.maskEl.unmask();
25320 setCanvasPosition : function()
25322 if(!this.canvasEl){
25326 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25327 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25329 this.previewEl.setLeft(pw);
25330 this.previewEl.setTop(ph);
25334 onMouseDown : function(e)
25338 this.dragable = true;
25339 this.pinching = false;
25341 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25342 this.dragable = false;
25346 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25347 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25351 onMouseMove : function(e)
25355 if(!this.canvasLoaded){
25359 if (!this.dragable){
25363 var minX = Math.ceil(this.thumbEl.getLeft(true));
25364 var minY = Math.ceil(this.thumbEl.getTop(true));
25366 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25367 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25369 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25370 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25372 x = x - this.mouseX;
25373 y = y - this.mouseY;
25375 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25376 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25378 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25379 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25381 this.previewEl.setLeft(bgX);
25382 this.previewEl.setTop(bgY);
25384 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25385 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25388 onMouseUp : function(e)
25392 this.dragable = false;
25395 onMouseWheel : function(e)
25399 this.startScale = this.scale;
25401 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25403 if(!this.zoomable()){
25404 this.scale = this.startScale;
25413 zoomable : function()
25415 var minScale = this.thumbEl.getWidth() / this.minWidth;
25417 if(this.minWidth < this.minHeight){
25418 minScale = this.thumbEl.getHeight() / this.minHeight;
25421 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25422 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25426 (this.rotate == 0 || this.rotate == 180) &&
25428 width > this.imageEl.OriginWidth ||
25429 height > this.imageEl.OriginHeight ||
25430 (width < this.minWidth && height < this.minHeight)
25438 (this.rotate == 90 || this.rotate == 270) &&
25440 width > this.imageEl.OriginWidth ||
25441 height > this.imageEl.OriginHeight ||
25442 (width < this.minHeight && height < this.minWidth)
25449 !this.isDocument &&
25450 (this.rotate == 0 || this.rotate == 180) &&
25452 width < this.minWidth ||
25453 width > this.imageEl.OriginWidth ||
25454 height < this.minHeight ||
25455 height > this.imageEl.OriginHeight
25462 !this.isDocument &&
25463 (this.rotate == 90 || this.rotate == 270) &&
25465 width < this.minHeight ||
25466 width > this.imageEl.OriginWidth ||
25467 height < this.minWidth ||
25468 height > this.imageEl.OriginHeight
25478 onRotateLeft : function(e)
25480 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25482 var minScale = this.thumbEl.getWidth() / this.minWidth;
25484 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25485 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25487 this.startScale = this.scale;
25489 while (this.getScaleLevel() < minScale){
25491 this.scale = this.scale + 1;
25493 if(!this.zoomable()){
25498 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25499 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25504 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25511 this.scale = this.startScale;
25513 this.onRotateFail();
25518 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25520 if(this.isDocument){
25521 this.setThumbBoxSize();
25522 this.setThumbBoxPosition();
25523 this.setCanvasPosition();
25528 this.fireEvent('rotate', this, 'left');
25532 onRotateRight : function(e)
25534 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25536 var minScale = this.thumbEl.getWidth() / this.minWidth;
25538 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25539 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25541 this.startScale = this.scale;
25543 while (this.getScaleLevel() < minScale){
25545 this.scale = this.scale + 1;
25547 if(!this.zoomable()){
25552 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25553 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25558 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25565 this.scale = this.startScale;
25567 this.onRotateFail();
25572 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25574 if(this.isDocument){
25575 this.setThumbBoxSize();
25576 this.setThumbBoxPosition();
25577 this.setCanvasPosition();
25582 this.fireEvent('rotate', this, 'right');
25585 onRotateFail : function()
25587 this.errorEl.show(true);
25591 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25596 this.previewEl.dom.innerHTML = '';
25598 var canvasEl = document.createElement("canvas");
25600 var contextEl = canvasEl.getContext("2d");
25602 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25603 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25604 var center = this.imageEl.OriginWidth / 2;
25606 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25607 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25608 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25609 center = this.imageEl.OriginHeight / 2;
25612 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25614 contextEl.translate(center, center);
25615 contextEl.rotate(this.rotate * Math.PI / 180);
25617 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25619 this.canvasEl = document.createElement("canvas");
25621 this.contextEl = this.canvasEl.getContext("2d");
25623 switch (this.rotate) {
25626 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25627 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25629 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25634 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25635 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25637 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25638 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);
25642 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25647 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25648 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25650 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25651 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);
25655 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);
25660 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25661 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25663 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25664 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25668 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);
25675 this.previewEl.appendChild(this.canvasEl);
25677 this.setCanvasPosition();
25682 if(!this.canvasLoaded){
25686 var imageCanvas = document.createElement("canvas");
25688 var imageContext = imageCanvas.getContext("2d");
25690 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25691 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25693 var center = imageCanvas.width / 2;
25695 imageContext.translate(center, center);
25697 imageContext.rotate(this.rotate * Math.PI / 180);
25699 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25701 var canvas = document.createElement("canvas");
25703 var context = canvas.getContext("2d");
25705 canvas.width = this.minWidth;
25706 canvas.height = this.minHeight;
25708 switch (this.rotate) {
25711 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25712 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25714 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25715 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25717 var targetWidth = this.minWidth - 2 * x;
25718 var targetHeight = this.minHeight - 2 * y;
25722 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25723 scale = targetWidth / width;
25726 if(x > 0 && y == 0){
25727 scale = targetHeight / height;
25730 if(x > 0 && y > 0){
25731 scale = targetWidth / width;
25733 if(width < height){
25734 scale = targetHeight / height;
25738 context.scale(scale, scale);
25740 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25741 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25743 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25744 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25746 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25751 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25752 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25754 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25755 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25757 var targetWidth = this.minWidth - 2 * x;
25758 var targetHeight = this.minHeight - 2 * y;
25762 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25763 scale = targetWidth / width;
25766 if(x > 0 && y == 0){
25767 scale = targetHeight / height;
25770 if(x > 0 && y > 0){
25771 scale = targetWidth / width;
25773 if(width < height){
25774 scale = targetHeight / height;
25778 context.scale(scale, scale);
25780 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25781 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25783 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25784 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25786 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25788 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25793 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25794 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25796 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25797 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25799 var targetWidth = this.minWidth - 2 * x;
25800 var targetHeight = this.minHeight - 2 * y;
25804 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25805 scale = targetWidth / width;
25808 if(x > 0 && y == 0){
25809 scale = targetHeight / height;
25812 if(x > 0 && y > 0){
25813 scale = targetWidth / width;
25815 if(width < height){
25816 scale = targetHeight / height;
25820 context.scale(scale, scale);
25822 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25823 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25825 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25826 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25828 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25829 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25831 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25836 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25837 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25839 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25840 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25842 var targetWidth = this.minWidth - 2 * x;
25843 var targetHeight = this.minHeight - 2 * y;
25847 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25848 scale = targetWidth / width;
25851 if(x > 0 && y == 0){
25852 scale = targetHeight / height;
25855 if(x > 0 && y > 0){
25856 scale = targetWidth / width;
25858 if(width < height){
25859 scale = targetHeight / height;
25863 context.scale(scale, scale);
25865 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25866 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25868 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25869 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25871 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25873 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25880 this.cropData = canvas.toDataURL(this.cropType);
25882 if(this.fireEvent('crop', this, this.cropData) !== false){
25883 this.process(this.file, this.cropData);
25890 setThumbBoxSize : function()
25894 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25895 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25896 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25898 this.minWidth = width;
25899 this.minHeight = height;
25901 if(this.rotate == 90 || this.rotate == 270){
25902 this.minWidth = height;
25903 this.minHeight = width;
25908 width = Math.ceil(this.minWidth * height / this.minHeight);
25910 if(this.minWidth > this.minHeight){
25912 height = Math.ceil(this.minHeight * width / this.minWidth);
25915 this.thumbEl.setStyle({
25916 width : width + 'px',
25917 height : height + 'px'
25924 setThumbBoxPosition : function()
25926 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25927 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25929 this.thumbEl.setLeft(x);
25930 this.thumbEl.setTop(y);
25934 baseRotateLevel : function()
25936 this.baseRotate = 1;
25939 typeof(this.exif) != 'undefined' &&
25940 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25941 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25943 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25946 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25950 baseScaleLevel : function()
25954 if(this.isDocument){
25956 if(this.baseRotate == 6 || this.baseRotate == 8){
25958 height = this.thumbEl.getHeight();
25959 this.baseScale = height / this.imageEl.OriginWidth;
25961 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25962 width = this.thumbEl.getWidth();
25963 this.baseScale = width / this.imageEl.OriginHeight;
25969 height = this.thumbEl.getHeight();
25970 this.baseScale = height / this.imageEl.OriginHeight;
25972 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25973 width = this.thumbEl.getWidth();
25974 this.baseScale = width / this.imageEl.OriginWidth;
25980 if(this.baseRotate == 6 || this.baseRotate == 8){
25982 width = this.thumbEl.getHeight();
25983 this.baseScale = width / this.imageEl.OriginHeight;
25985 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25986 height = this.thumbEl.getWidth();
25987 this.baseScale = height / this.imageEl.OriginHeight;
25990 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25991 height = this.thumbEl.getWidth();
25992 this.baseScale = height / this.imageEl.OriginHeight;
25994 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25995 width = this.thumbEl.getHeight();
25996 this.baseScale = width / this.imageEl.OriginWidth;
26003 width = this.thumbEl.getWidth();
26004 this.baseScale = width / this.imageEl.OriginWidth;
26006 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26007 height = this.thumbEl.getHeight();
26008 this.baseScale = height / this.imageEl.OriginHeight;
26011 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26013 height = this.thumbEl.getHeight();
26014 this.baseScale = height / this.imageEl.OriginHeight;
26016 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26017 width = this.thumbEl.getWidth();
26018 this.baseScale = width / this.imageEl.OriginWidth;
26026 getScaleLevel : function()
26028 return this.baseScale * Math.pow(1.1, this.scale);
26031 onTouchStart : function(e)
26033 if(!this.canvasLoaded){
26034 this.beforeSelectFile(e);
26038 var touches = e.browserEvent.touches;
26044 if(touches.length == 1){
26045 this.onMouseDown(e);
26049 if(touches.length != 2){
26055 for(var i = 0, finger; finger = touches[i]; i++){
26056 coords.push(finger.pageX, finger.pageY);
26059 var x = Math.pow(coords[0] - coords[2], 2);
26060 var y = Math.pow(coords[1] - coords[3], 2);
26062 this.startDistance = Math.sqrt(x + y);
26064 this.startScale = this.scale;
26066 this.pinching = true;
26067 this.dragable = false;
26071 onTouchMove : function(e)
26073 if(!this.pinching && !this.dragable){
26077 var touches = e.browserEvent.touches;
26084 this.onMouseMove(e);
26090 for(var i = 0, finger; finger = touches[i]; i++){
26091 coords.push(finger.pageX, finger.pageY);
26094 var x = Math.pow(coords[0] - coords[2], 2);
26095 var y = Math.pow(coords[1] - coords[3], 2);
26097 this.endDistance = Math.sqrt(x + y);
26099 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26101 if(!this.zoomable()){
26102 this.scale = this.startScale;
26110 onTouchEnd : function(e)
26112 this.pinching = false;
26113 this.dragable = false;
26117 process : function(file, crop)
26120 this.maskEl.mask(this.loadingText);
26123 this.xhr = new XMLHttpRequest();
26125 file.xhr = this.xhr;
26127 this.xhr.open(this.method, this.url, true);
26130 "Accept": "application/json",
26131 "Cache-Control": "no-cache",
26132 "X-Requested-With": "XMLHttpRequest"
26135 for (var headerName in headers) {
26136 var headerValue = headers[headerName];
26138 this.xhr.setRequestHeader(headerName, headerValue);
26144 this.xhr.onload = function()
26146 _this.xhrOnLoad(_this.xhr);
26149 this.xhr.onerror = function()
26151 _this.xhrOnError(_this.xhr);
26154 var formData = new FormData();
26156 formData.append('returnHTML', 'NO');
26159 formData.append('crop', crop);
26162 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26163 formData.append(this.paramName, file, file.name);
26166 if(typeof(file.filename) != 'undefined'){
26167 formData.append('filename', file.filename);
26170 if(typeof(file.mimetype) != 'undefined'){
26171 formData.append('mimetype', file.mimetype);
26174 if(this.fireEvent('arrange', this, formData) != false){
26175 this.xhr.send(formData);
26179 xhrOnLoad : function(xhr)
26182 this.maskEl.unmask();
26185 if (xhr.readyState !== 4) {
26186 this.fireEvent('exception', this, xhr);
26190 var response = Roo.decode(xhr.responseText);
26192 if(!response.success){
26193 this.fireEvent('exception', this, xhr);
26197 var response = Roo.decode(xhr.responseText);
26199 this.fireEvent('upload', this, response);
26203 xhrOnError : function()
26206 this.maskEl.unmask();
26209 Roo.log('xhr on error');
26211 var response = Roo.decode(xhr.responseText);
26217 prepare : function(file)
26220 this.maskEl.mask(this.loadingText);
26226 if(typeof(file) === 'string'){
26227 this.loadCanvas(file);
26231 if(!file || !this.urlAPI){
26236 this.cropType = file.type;
26240 if(this.fireEvent('prepare', this, this.file) != false){
26242 var reader = new FileReader();
26244 reader.onload = function (e) {
26245 if (e.target.error) {
26246 Roo.log(e.target.error);
26250 var buffer = e.target.result,
26251 dataView = new DataView(buffer),
26253 maxOffset = dataView.byteLength - 4,
26257 if (dataView.getUint16(0) === 0xffd8) {
26258 while (offset < maxOffset) {
26259 markerBytes = dataView.getUint16(offset);
26261 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26262 markerLength = dataView.getUint16(offset + 2) + 2;
26263 if (offset + markerLength > dataView.byteLength) {
26264 Roo.log('Invalid meta data: Invalid segment size.');
26268 if(markerBytes == 0xffe1){
26269 _this.parseExifData(
26276 offset += markerLength;
26286 var url = _this.urlAPI.createObjectURL(_this.file);
26288 _this.loadCanvas(url);
26293 reader.readAsArrayBuffer(this.file);
26299 parseExifData : function(dataView, offset, length)
26301 var tiffOffset = offset + 10,
26305 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26306 // No Exif data, might be XMP data instead
26310 // Check for the ASCII code for "Exif" (0x45786966):
26311 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26312 // No Exif data, might be XMP data instead
26315 if (tiffOffset + 8 > dataView.byteLength) {
26316 Roo.log('Invalid Exif data: Invalid segment size.');
26319 // Check for the two null bytes:
26320 if (dataView.getUint16(offset + 8) !== 0x0000) {
26321 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26324 // Check the byte alignment:
26325 switch (dataView.getUint16(tiffOffset)) {
26327 littleEndian = true;
26330 littleEndian = false;
26333 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26336 // Check for the TIFF tag marker (0x002A):
26337 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26338 Roo.log('Invalid Exif data: Missing TIFF marker.');
26341 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26342 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26344 this.parseExifTags(
26347 tiffOffset + dirOffset,
26352 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26357 if (dirOffset + 6 > dataView.byteLength) {
26358 Roo.log('Invalid Exif data: Invalid directory offset.');
26361 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26362 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26363 if (dirEndOffset + 4 > dataView.byteLength) {
26364 Roo.log('Invalid Exif data: Invalid directory size.');
26367 for (i = 0; i < tagsNumber; i += 1) {
26371 dirOffset + 2 + 12 * i, // tag offset
26375 // Return the offset to the next directory:
26376 return dataView.getUint32(dirEndOffset, littleEndian);
26379 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26381 var tag = dataView.getUint16(offset, littleEndian);
26383 this.exif[tag] = this.getExifValue(
26387 dataView.getUint16(offset + 2, littleEndian), // tag type
26388 dataView.getUint32(offset + 4, littleEndian), // tag length
26393 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26395 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26404 Roo.log('Invalid Exif data: Invalid tag type.');
26408 tagSize = tagType.size * length;
26409 // Determine if the value is contained in the dataOffset bytes,
26410 // or if the value at the dataOffset is a pointer to the actual data:
26411 dataOffset = tagSize > 4 ?
26412 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26413 if (dataOffset + tagSize > dataView.byteLength) {
26414 Roo.log('Invalid Exif data: Invalid data offset.');
26417 if (length === 1) {
26418 return tagType.getValue(dataView, dataOffset, littleEndian);
26421 for (i = 0; i < length; i += 1) {
26422 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26425 if (tagType.ascii) {
26427 // Concatenate the chars:
26428 for (i = 0; i < values.length; i += 1) {
26430 // Ignore the terminating NULL byte(s):
26431 if (c === '\u0000') {
26443 Roo.apply(Roo.bootstrap.UploadCropbox, {
26445 'Orientation': 0x0112
26449 1: 0, //'top-left',
26451 3: 180, //'bottom-right',
26452 // 4: 'bottom-left',
26454 6: 90, //'right-top',
26455 // 7: 'right-bottom',
26456 8: 270 //'left-bottom'
26460 // byte, 8-bit unsigned int:
26462 getValue: function (dataView, dataOffset) {
26463 return dataView.getUint8(dataOffset);
26467 // ascii, 8-bit byte:
26469 getValue: function (dataView, dataOffset) {
26470 return String.fromCharCode(dataView.getUint8(dataOffset));
26475 // short, 16 bit int:
26477 getValue: function (dataView, dataOffset, littleEndian) {
26478 return dataView.getUint16(dataOffset, littleEndian);
26482 // long, 32 bit int:
26484 getValue: function (dataView, dataOffset, littleEndian) {
26485 return dataView.getUint32(dataOffset, littleEndian);
26489 // rational = two long values, first is numerator, second is denominator:
26491 getValue: function (dataView, dataOffset, littleEndian) {
26492 return dataView.getUint32(dataOffset, littleEndian) /
26493 dataView.getUint32(dataOffset + 4, littleEndian);
26497 // slong, 32 bit signed int:
26499 getValue: function (dataView, dataOffset, littleEndian) {
26500 return dataView.getInt32(dataOffset, littleEndian);
26504 // srational, two slongs, first is numerator, second is denominator:
26506 getValue: function (dataView, dataOffset, littleEndian) {
26507 return dataView.getInt32(dataOffset, littleEndian) /
26508 dataView.getInt32(dataOffset + 4, littleEndian);
26518 cls : 'btn-group roo-upload-cropbox-rotate-left',
26519 action : 'rotate-left',
26523 cls : 'btn btn-default',
26524 html : '<i class="fa fa-undo"></i>'
26530 cls : 'btn-group roo-upload-cropbox-picture',
26531 action : 'picture',
26535 cls : 'btn btn-default',
26536 html : '<i class="fa fa-picture-o"></i>'
26542 cls : 'btn-group roo-upload-cropbox-rotate-right',
26543 action : 'rotate-right',
26547 cls : 'btn btn-default',
26548 html : '<i class="fa fa-repeat"></i>'
26556 cls : 'btn-group roo-upload-cropbox-rotate-left',
26557 action : 'rotate-left',
26561 cls : 'btn btn-default',
26562 html : '<i class="fa fa-undo"></i>'
26568 cls : 'btn-group roo-upload-cropbox-download',
26569 action : 'download',
26573 cls : 'btn btn-default',
26574 html : '<i class="fa fa-download"></i>'
26580 cls : 'btn-group roo-upload-cropbox-crop',
26585 cls : 'btn btn-default',
26586 html : '<i class="fa fa-crop"></i>'
26592 cls : 'btn-group roo-upload-cropbox-trash',
26597 cls : 'btn btn-default',
26598 html : '<i class="fa fa-trash"></i>'
26604 cls : 'btn-group roo-upload-cropbox-rotate-right',
26605 action : 'rotate-right',
26609 cls : 'btn btn-default',
26610 html : '<i class="fa fa-repeat"></i>'
26618 cls : 'btn-group roo-upload-cropbox-rotate-left',
26619 action : 'rotate-left',
26623 cls : 'btn btn-default',
26624 html : '<i class="fa fa-undo"></i>'
26630 cls : 'btn-group roo-upload-cropbox-rotate-right',
26631 action : 'rotate-right',
26635 cls : 'btn btn-default',
26636 html : '<i class="fa fa-repeat"></i>'
26649 * @class Roo.bootstrap.DocumentManager
26650 * @extends Roo.bootstrap.Component
26651 * Bootstrap DocumentManager class
26652 * @cfg {String} paramName default 'imageUpload'
26653 * @cfg {String} method default POST
26654 * @cfg {String} url action url
26655 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26656 * @cfg {Boolean} multiple multiple upload default true
26657 * @cfg {Number} thumbSize default 300
26658 * @cfg {String} fieldLabel
26659 * @cfg {Number} labelWidth default 4
26660 * @cfg {String} labelAlign (left|top) default left
26661 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26664 * Create a new DocumentManager
26665 * @param {Object} config The config object
26668 Roo.bootstrap.DocumentManager = function(config){
26669 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26674 * Fire when initial the DocumentManager
26675 * @param {Roo.bootstrap.DocumentManager} this
26680 * inspect selected file
26681 * @param {Roo.bootstrap.DocumentManager} this
26682 * @param {File} file
26687 * Fire when xhr load exception
26688 * @param {Roo.bootstrap.DocumentManager} this
26689 * @param {XMLHttpRequest} xhr
26691 "exception" : true,
26694 * prepare the form data
26695 * @param {Roo.bootstrap.DocumentManager} this
26696 * @param {Object} formData
26701 * Fire when remove the file
26702 * @param {Roo.bootstrap.DocumentManager} this
26703 * @param {Object} file
26708 * Fire after refresh the file
26709 * @param {Roo.bootstrap.DocumentManager} this
26714 * Fire after click the image
26715 * @param {Roo.bootstrap.DocumentManager} this
26716 * @param {Object} file
26721 * Fire when upload a image and editable set to true
26722 * @param {Roo.bootstrap.DocumentManager} this
26723 * @param {Object} file
26727 * @event beforeselectfile
26728 * Fire before select file
26729 * @param {Roo.bootstrap.DocumentManager} this
26731 "beforeselectfile" : true,
26734 * Fire before process file
26735 * @param {Roo.bootstrap.DocumentManager} this
26736 * @param {Object} file
26743 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26752 paramName : 'imageUpload',
26755 labelAlign : 'left',
26762 getAutoCreate : function()
26764 var managerWidget = {
26766 cls : 'roo-document-manager',
26770 cls : 'roo-document-manager-selector',
26775 cls : 'roo-document-manager-uploader',
26779 cls : 'roo-document-manager-upload-btn',
26780 html : '<i class="fa fa-plus"></i>'
26791 cls : 'column col-md-12',
26796 if(this.fieldLabel.length){
26801 cls : 'column col-md-12',
26802 html : this.fieldLabel
26806 cls : 'column col-md-12',
26811 if(this.labelAlign == 'left'){
26815 cls : 'column col-md-' + this.labelWidth,
26816 html : this.fieldLabel
26820 cls : 'column col-md-' + (12 - this.labelWidth),
26830 cls : 'row clearfix',
26838 initEvents : function()
26840 this.managerEl = this.el.select('.roo-document-manager', true).first();
26841 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26843 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26844 this.selectorEl.hide();
26847 this.selectorEl.attr('multiple', 'multiple');
26850 this.selectorEl.on('change', this.onFileSelected, this);
26852 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26853 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26855 this.uploader.on('click', this.onUploaderClick, this);
26857 this.renderProgressDialog();
26861 window.addEventListener("resize", function() { _this.refresh(); } );
26863 this.fireEvent('initial', this);
26866 renderProgressDialog : function()
26870 this.progressDialog = new Roo.bootstrap.Modal({
26871 cls : 'roo-document-manager-progress-dialog',
26872 allow_close : false,
26882 btnclick : function() {
26883 _this.uploadCancel();
26889 this.progressDialog.render(Roo.get(document.body));
26891 this.progress = new Roo.bootstrap.Progress({
26892 cls : 'roo-document-manager-progress',
26897 this.progress.render(this.progressDialog.getChildContainer());
26899 this.progressBar = new Roo.bootstrap.ProgressBar({
26900 cls : 'roo-document-manager-progress-bar',
26903 aria_valuemax : 12,
26907 this.progressBar.render(this.progress.getChildContainer());
26910 onUploaderClick : function(e)
26912 e.preventDefault();
26914 if(this.fireEvent('beforeselectfile', this) != false){
26915 this.selectorEl.dom.click();
26920 onFileSelected : function(e)
26922 e.preventDefault();
26924 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26928 Roo.each(this.selectorEl.dom.files, function(file){
26929 if(this.fireEvent('inspect', this, file) != false){
26930 this.files.push(file);
26940 this.selectorEl.dom.value = '';
26942 if(!this.files.length){
26946 if(this.boxes > 0 && this.files.length > this.boxes){
26947 this.files = this.files.slice(0, this.boxes);
26950 this.uploader.show();
26952 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26953 this.uploader.hide();
26962 Roo.each(this.files, function(file){
26964 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26965 var f = this.renderPreview(file);
26970 if(file.type.indexOf('image') != -1){
26971 this.delegates.push(
26973 _this.process(file);
26974 }).createDelegate(this)
26982 _this.process(file);
26983 }).createDelegate(this)
26988 this.files = files;
26990 this.delegates = this.delegates.concat(docs);
26992 if(!this.delegates.length){
26997 this.progressBar.aria_valuemax = this.delegates.length;
27004 arrange : function()
27006 if(!this.delegates.length){
27007 this.progressDialog.hide();
27012 var delegate = this.delegates.shift();
27014 this.progressDialog.show();
27016 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27018 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27023 refresh : function()
27025 this.uploader.show();
27027 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27028 this.uploader.hide();
27031 Roo.isTouch ? this.closable(false) : this.closable(true);
27033 this.fireEvent('refresh', this);
27036 onRemove : function(e, el, o)
27038 e.preventDefault();
27040 this.fireEvent('remove', this, o);
27044 remove : function(o)
27048 Roo.each(this.files, function(file){
27049 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27058 this.files = files;
27065 Roo.each(this.files, function(file){
27070 file.target.remove();
27079 onClick : function(e, el, o)
27081 e.preventDefault();
27083 this.fireEvent('click', this, o);
27087 closable : function(closable)
27089 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27091 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27103 xhrOnLoad : function(xhr)
27105 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27109 if (xhr.readyState !== 4) {
27111 this.fireEvent('exception', this, xhr);
27115 var response = Roo.decode(xhr.responseText);
27117 if(!response.success){
27119 this.fireEvent('exception', this, xhr);
27123 var file = this.renderPreview(response.data);
27125 this.files.push(file);
27131 xhrOnError : function(xhr)
27133 Roo.log('xhr on error');
27135 var response = Roo.decode(xhr.responseText);
27142 process : function(file)
27144 if(this.fireEvent('process', this, file) !== false){
27145 if(this.editable && file.type.indexOf('image') != -1){
27146 this.fireEvent('edit', this, file);
27150 this.uploadStart(file, false);
27157 uploadStart : function(file, crop)
27159 this.xhr = new XMLHttpRequest();
27161 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27166 file.xhr = this.xhr;
27168 this.managerEl.createChild({
27170 cls : 'roo-document-manager-loading',
27174 tooltip : file.name,
27175 cls : 'roo-document-manager-thumb',
27176 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27182 this.xhr.open(this.method, this.url, true);
27185 "Accept": "application/json",
27186 "Cache-Control": "no-cache",
27187 "X-Requested-With": "XMLHttpRequest"
27190 for (var headerName in headers) {
27191 var headerValue = headers[headerName];
27193 this.xhr.setRequestHeader(headerName, headerValue);
27199 this.xhr.onload = function()
27201 _this.xhrOnLoad(_this.xhr);
27204 this.xhr.onerror = function()
27206 _this.xhrOnError(_this.xhr);
27209 var formData = new FormData();
27211 formData.append('returnHTML', 'NO');
27214 formData.append('crop', crop);
27217 formData.append(this.paramName, file, file.name);
27219 if(this.fireEvent('prepare', this, formData) != false){
27220 this.xhr.send(formData);
27224 uploadCancel : function()
27231 this.delegates = [];
27233 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27240 renderPreview : function(file)
27242 if(typeof(file.target) != 'undefined' && file.target){
27246 var previewEl = this.managerEl.createChild({
27248 cls : 'roo-document-manager-preview',
27252 tooltip : file.filename,
27253 cls : 'roo-document-manager-thumb',
27254 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27259 html : '<i class="fa fa-times-circle"></i>'
27264 var close = previewEl.select('button.close', true).first();
27266 close.on('click', this.onRemove, this, file);
27268 file.target = previewEl;
27270 var image = previewEl.select('img', true).first();
27274 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27276 image.on('click', this.onClick, this, file);
27282 onPreviewLoad : function(file, image)
27284 if(typeof(file.target) == 'undefined' || !file.target){
27288 var width = image.dom.naturalWidth || image.dom.width;
27289 var height = image.dom.naturalHeight || image.dom.height;
27291 if(width > height){
27292 file.target.addClass('wide');
27296 file.target.addClass('tall');
27301 uploadFromSource : function(file, crop)
27303 this.xhr = new XMLHttpRequest();
27305 this.managerEl.createChild({
27307 cls : 'roo-document-manager-loading',
27311 tooltip : file.name,
27312 cls : 'roo-document-manager-thumb',
27313 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27319 this.xhr.open(this.method, this.url, true);
27322 "Accept": "application/json",
27323 "Cache-Control": "no-cache",
27324 "X-Requested-With": "XMLHttpRequest"
27327 for (var headerName in headers) {
27328 var headerValue = headers[headerName];
27330 this.xhr.setRequestHeader(headerName, headerValue);
27336 this.xhr.onload = function()
27338 _this.xhrOnLoad(_this.xhr);
27341 this.xhr.onerror = function()
27343 _this.xhrOnError(_this.xhr);
27346 var formData = new FormData();
27348 formData.append('returnHTML', 'NO');
27350 formData.append('crop', crop);
27352 if(typeof(file.filename) != 'undefined'){
27353 formData.append('filename', file.filename);
27356 if(typeof(file.mimetype) != 'undefined'){
27357 formData.append('mimetype', file.mimetype);
27360 if(this.fireEvent('prepare', this, formData) != false){
27361 this.xhr.send(formData);
27371 * @class Roo.bootstrap.DocumentViewer
27372 * @extends Roo.bootstrap.Component
27373 * Bootstrap DocumentViewer class
27376 * Create a new DocumentViewer
27377 * @param {Object} config The config object
27380 Roo.bootstrap.DocumentViewer = function(config){
27381 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27386 * Fire after initEvent
27387 * @param {Roo.bootstrap.DocumentViewer} this
27393 * @param {Roo.bootstrap.DocumentViewer} this
27398 * Fire after trash button
27399 * @param {Roo.bootstrap.DocumentViewer} this
27406 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27408 getAutoCreate : function()
27412 cls : 'roo-document-viewer',
27416 cls : 'roo-document-viewer-body',
27420 cls : 'roo-document-viewer-thumb',
27424 cls : 'roo-document-viewer-image'
27432 cls : 'roo-document-viewer-footer',
27435 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27443 cls : 'btn btn-default roo-document-viewer-trash',
27444 html : '<i class="fa fa-trash"></i>'
27457 initEvents : function()
27460 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27461 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27463 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27464 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27466 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27467 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27469 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27470 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27472 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27473 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27475 this.bodyEl.on('click', this.onClick, this);
27477 this.trashBtn.on('click', this.onTrash, this);
27481 initial : function()
27483 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27486 this.fireEvent('initial', this);
27490 onClick : function(e)
27492 e.preventDefault();
27494 this.fireEvent('click', this);
27497 onTrash : function(e)
27499 e.preventDefault();
27501 this.fireEvent('trash', this);
27513 * @class Roo.bootstrap.NavProgressBar
27514 * @extends Roo.bootstrap.Component
27515 * Bootstrap NavProgressBar class
27518 * Create a new nav progress bar
27519 * @param {Object} config The config object
27522 Roo.bootstrap.NavProgressBar = function(config){
27523 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27525 this.bullets = this.bullets || [];
27527 // Roo.bootstrap.NavProgressBar.register(this);
27531 * Fires when the active item changes
27532 * @param {Roo.bootstrap.NavProgressBar} this
27533 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27534 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27541 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27546 getAutoCreate : function()
27548 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27552 cls : 'roo-navigation-bar-group',
27556 cls : 'roo-navigation-top-bar'
27560 cls : 'roo-navigation-bullets-bar',
27564 cls : 'roo-navigation-bar'
27571 cls : 'roo-navigation-bottom-bar'
27581 initEvents: function()
27586 onRender : function(ct, position)
27588 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27590 if(this.bullets.length){
27591 Roo.each(this.bullets, function(b){
27600 addItem : function(cfg)
27602 var item = new Roo.bootstrap.NavProgressItem(cfg);
27604 item.parentId = this.id;
27605 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27608 var top = new Roo.bootstrap.Element({
27610 cls : 'roo-navigation-bar-text'
27613 var bottom = new Roo.bootstrap.Element({
27615 cls : 'roo-navigation-bar-text'
27618 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27619 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27621 var topText = new Roo.bootstrap.Element({
27623 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27626 var bottomText = new Roo.bootstrap.Element({
27628 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27631 topText.onRender(top.el, null);
27632 bottomText.onRender(bottom.el, null);
27635 item.bottomEl = bottom;
27638 this.barItems.push(item);
27643 getActive : function()
27645 var active = false;
27647 Roo.each(this.barItems, function(v){
27649 if (!v.isActive()) {
27661 setActiveItem : function(item)
27665 Roo.each(this.barItems, function(v){
27666 if (v.rid == item.rid) {
27670 if (v.isActive()) {
27671 v.setActive(false);
27676 item.setActive(true);
27678 this.fireEvent('changed', this, item, prev);
27681 getBarItem: function(rid)
27685 Roo.each(this.barItems, function(e) {
27686 if (e.rid != rid) {
27697 indexOfItem : function(item)
27701 Roo.each(this.barItems, function(v, i){
27703 if (v.rid != item.rid) {
27714 setActiveNext : function()
27716 var i = this.indexOfItem(this.getActive());
27718 if (i > this.barItems.length) {
27722 this.setActiveItem(this.barItems[i+1]);
27725 setActivePrev : function()
27727 var i = this.indexOfItem(this.getActive());
27733 this.setActiveItem(this.barItems[i-1]);
27736 format : function()
27738 if(!this.barItems.length){
27742 var width = 100 / this.barItems.length;
27744 Roo.each(this.barItems, function(i){
27745 i.el.setStyle('width', width + '%');
27746 i.topEl.el.setStyle('width', width + '%');
27747 i.bottomEl.el.setStyle('width', width + '%');
27756 * Nav Progress Item
27761 * @class Roo.bootstrap.NavProgressItem
27762 * @extends Roo.bootstrap.Component
27763 * Bootstrap NavProgressItem class
27764 * @cfg {String} rid the reference id
27765 * @cfg {Boolean} active (true|false) Is item active default false
27766 * @cfg {Boolean} disabled (true|false) Is item active default false
27767 * @cfg {String} html
27768 * @cfg {String} position (top|bottom) text position default bottom
27769 * @cfg {String} icon show icon instead of number
27772 * Create a new NavProgressItem
27773 * @param {Object} config The config object
27775 Roo.bootstrap.NavProgressItem = function(config){
27776 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27781 * The raw click event for the entire grid.
27782 * @param {Roo.bootstrap.NavProgressItem} this
27783 * @param {Roo.EventObject} e
27790 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27796 position : 'bottom',
27799 getAutoCreate : function()
27801 var iconCls = 'roo-navigation-bar-item-icon';
27803 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27807 cls: 'roo-navigation-bar-item',
27817 cfg.cls += ' active';
27820 cfg.cls += ' disabled';
27826 disable : function()
27828 this.setDisabled(true);
27831 enable : function()
27833 this.setDisabled(false);
27836 initEvents: function()
27838 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27840 this.iconEl.on('click', this.onClick, this);
27843 onClick : function(e)
27845 e.preventDefault();
27851 if(this.fireEvent('click', this, e) === false){
27855 this.parent().setActiveItem(this);
27858 isActive: function ()
27860 return this.active;
27863 setActive : function(state)
27865 if(this.active == state){
27869 this.active = state;
27872 this.el.addClass('active');
27876 this.el.removeClass('active');
27881 setDisabled : function(state)
27883 if(this.disabled == state){
27887 this.disabled = state;
27890 this.el.addClass('disabled');
27894 this.el.removeClass('disabled');
27897 tooltipEl : function()
27899 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27912 * @class Roo.bootstrap.FieldLabel
27913 * @extends Roo.bootstrap.Component
27914 * Bootstrap FieldLabel class
27915 * @cfg {String} html contents of the element
27916 * @cfg {String} tag tag of the element default label
27917 * @cfg {String} cls class of the element
27918 * @cfg {String} target label target
27919 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27920 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27921 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27922 * @cfg {String} iconTooltip default "This field is required"
27925 * Create a new FieldLabel
27926 * @param {Object} config The config object
27929 Roo.bootstrap.FieldLabel = function(config){
27930 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27935 * Fires after the field has been marked as invalid.
27936 * @param {Roo.form.FieldLabel} this
27937 * @param {String} msg The validation message
27942 * Fires after the field has been validated with no errors.
27943 * @param {Roo.form.FieldLabel} this
27949 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27956 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27957 validClass : 'text-success fa fa-lg fa-check',
27958 iconTooltip : 'This field is required',
27960 getAutoCreate : function(){
27964 cls : 'roo-bootstrap-field-label ' + this.cls,
27970 tooltip : this.iconTooltip
27982 initEvents: function()
27984 Roo.bootstrap.Element.superclass.initEvents.call(this);
27986 this.iconEl = this.el.select('i', true).first();
27988 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27990 Roo.bootstrap.FieldLabel.register(this);
27994 * Mark this field as valid
27996 markValid : function()
27998 this.iconEl.show();
28000 this.iconEl.removeClass(this.invalidClass);
28002 this.iconEl.addClass(this.validClass);
28004 this.fireEvent('valid', this);
28008 * Mark this field as invalid
28009 * @param {String} msg The validation message
28011 markInvalid : function(msg)
28013 this.iconEl.show();
28015 this.iconEl.removeClass(this.validClass);
28017 this.iconEl.addClass(this.invalidClass);
28019 this.fireEvent('invalid', this, msg);
28025 Roo.apply(Roo.bootstrap.FieldLabel, {
28030 * register a FieldLabel Group
28031 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28033 register : function(label)
28035 if(this.groups.hasOwnProperty(label.target)){
28039 this.groups[label.target] = label;
28043 * fetch a FieldLabel Group based on the target
28044 * @param {string} target
28045 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28047 get: function(target) {
28048 if (typeof(this.groups[target]) == 'undefined') {
28052 return this.groups[target] ;
28061 * page DateSplitField.
28067 * @class Roo.bootstrap.DateSplitField
28068 * @extends Roo.bootstrap.Component
28069 * Bootstrap DateSplitField class
28070 * @cfg {string} fieldLabel - the label associated
28071 * @cfg {Number} labelWidth set the width of label (0-12)
28072 * @cfg {String} labelAlign (top|left)
28073 * @cfg {Boolean} dayAllowBlank (true|false) default false
28074 * @cfg {Boolean} monthAllowBlank (true|false) default false
28075 * @cfg {Boolean} yearAllowBlank (true|false) default false
28076 * @cfg {string} dayPlaceholder
28077 * @cfg {string} monthPlaceholder
28078 * @cfg {string} yearPlaceholder
28079 * @cfg {string} dayFormat default 'd'
28080 * @cfg {string} monthFormat default 'm'
28081 * @cfg {string} yearFormat default 'Y'
28085 * Create a new DateSplitField
28086 * @param {Object} config The config object
28089 Roo.bootstrap.DateSplitField = function(config){
28090 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28096 * getting the data of years
28097 * @param {Roo.bootstrap.DateSplitField} this
28098 * @param {Object} years
28103 * getting the data of days
28104 * @param {Roo.bootstrap.DateSplitField} this
28105 * @param {Object} days
28110 * Fires after the field has been marked as invalid.
28111 * @param {Roo.form.Field} this
28112 * @param {String} msg The validation message
28117 * Fires after the field has been validated with no errors.
28118 * @param {Roo.form.Field} this
28124 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28127 labelAlign : 'top',
28129 dayAllowBlank : false,
28130 monthAllowBlank : false,
28131 yearAllowBlank : false,
28132 dayPlaceholder : '',
28133 monthPlaceholder : '',
28134 yearPlaceholder : '',
28138 isFormField : true,
28140 getAutoCreate : function()
28144 cls : 'row roo-date-split-field-group',
28149 cls : 'form-hidden-field roo-date-split-field-group-value',
28155 if(this.fieldLabel){
28158 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28162 html : this.fieldLabel
28168 Roo.each(['day', 'month', 'year'], function(t){
28171 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28178 inputEl: function ()
28180 return this.el.select('.roo-date-split-field-group-value', true).first();
28183 onRender : function(ct, position)
28187 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28189 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28191 this.dayField = new Roo.bootstrap.ComboBox({
28192 allowBlank : this.dayAllowBlank,
28193 alwaysQuery : true,
28194 displayField : 'value',
28197 forceSelection : true,
28199 placeholder : this.dayPlaceholder,
28200 selectOnFocus : true,
28201 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28202 triggerAction : 'all',
28204 valueField : 'value',
28205 store : new Roo.data.SimpleStore({
28206 data : (function() {
28208 _this.fireEvent('days', _this, days);
28211 fields : [ 'value' ]
28214 select : function (_self, record, index)
28216 _this.setValue(_this.getValue());
28221 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28223 this.monthField = new Roo.bootstrap.MonthField({
28224 after : '<i class=\"fa fa-calendar\"></i>',
28225 allowBlank : this.monthAllowBlank,
28226 placeholder : this.monthPlaceholder,
28229 render : function (_self)
28231 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28232 e.preventDefault();
28236 select : function (_self, oldvalue, newvalue)
28238 _this.setValue(_this.getValue());
28243 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28245 this.yearField = new Roo.bootstrap.ComboBox({
28246 allowBlank : this.yearAllowBlank,
28247 alwaysQuery : true,
28248 displayField : 'value',
28251 forceSelection : true,
28253 placeholder : this.yearPlaceholder,
28254 selectOnFocus : true,
28255 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28256 triggerAction : 'all',
28258 valueField : 'value',
28259 store : new Roo.data.SimpleStore({
28260 data : (function() {
28262 _this.fireEvent('years', _this, years);
28265 fields : [ 'value' ]
28268 select : function (_self, record, index)
28270 _this.setValue(_this.getValue());
28275 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28278 setValue : function(v, format)
28280 this.inputEl.dom.value = v;
28282 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28284 var d = Date.parseDate(v, f);
28291 this.setDay(d.format(this.dayFormat));
28292 this.setMonth(d.format(this.monthFormat));
28293 this.setYear(d.format(this.yearFormat));
28300 setDay : function(v)
28302 this.dayField.setValue(v);
28303 this.inputEl.dom.value = this.getValue();
28308 setMonth : function(v)
28310 this.monthField.setValue(v, true);
28311 this.inputEl.dom.value = this.getValue();
28316 setYear : function(v)
28318 this.yearField.setValue(v);
28319 this.inputEl.dom.value = this.getValue();
28324 getDay : function()
28326 return this.dayField.getValue();
28329 getMonth : function()
28331 return this.monthField.getValue();
28334 getYear : function()
28336 return this.yearField.getValue();
28339 getValue : function()
28341 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28343 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28353 this.inputEl.dom.value = '';
28358 validate : function()
28360 var d = this.dayField.validate();
28361 var m = this.monthField.validate();
28362 var y = this.yearField.validate();
28367 (!this.dayAllowBlank && !d) ||
28368 (!this.monthAllowBlank && !m) ||
28369 (!this.yearAllowBlank && !y)
28374 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28383 this.markInvalid();
28388 markValid : function()
28391 var label = this.el.select('label', true).first();
28392 var icon = this.el.select('i.fa-star', true).first();
28398 this.fireEvent('valid', this);
28402 * Mark this field as invalid
28403 * @param {String} msg The validation message
28405 markInvalid : function(msg)
28408 var label = this.el.select('label', true).first();
28409 var icon = this.el.select('i.fa-star', true).first();
28411 if(label && !icon){
28412 this.el.select('.roo-date-split-field-label', true).createChild({
28414 cls : 'text-danger fa fa-lg fa-star',
28415 tooltip : 'This field is required',
28416 style : 'margin-right:5px;'
28420 this.fireEvent('invalid', this, msg);
28423 clearInvalid : function()
28425 var label = this.el.select('label', true).first();
28426 var icon = this.el.select('i.fa-star', true).first();
28432 this.fireEvent('valid', this);
28435 getName: function()
28445 * http://masonry.desandro.com
28447 * The idea is to render all the bricks based on vertical width...
28449 * The original code extends 'outlayer' - we might need to use that....
28455 * @class Roo.bootstrap.LayoutMasonry
28456 * @extends Roo.bootstrap.Component
28457 * Bootstrap Layout Masonry class
28460 * Create a new Element
28461 * @param {Object} config The config object
28464 Roo.bootstrap.LayoutMasonry = function(config){
28465 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28471 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
28474 * @cfg {Boolean} isLayoutInstant = no animation?
28476 isLayoutInstant : false, // needed?
28479 * @cfg {Number} boxWidth width of the columns
28484 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
28489 * @cfg {Number} padWidth padding below box..
28494 * @cfg {Number} gutter gutter width..
28499 * @cfg {Number} maxCols maximum number of columns
28505 * @cfg {Boolean} isAutoInitial defalut true
28507 isAutoInitial : true,
28512 * @cfg {Boolean} isHorizontal defalut false
28514 isHorizontal : false,
28516 currentSize : null,
28522 bricks: null, //CompositeElement
28526 _isLayoutInited : false,
28528 // isAlternative : false, // only use for vertical layout...
28531 * @cfg {Number} alternativePadWidth padding below box..
28533 alternativePadWidth : 50,
28535 getAutoCreate : function(){
28539 cls: 'blog-masonary-wrapper ' + this.cls,
28541 cls : 'mas-boxes masonary'
28548 getChildContainer: function( )
28550 if (this.boxesEl) {
28551 return this.boxesEl;
28554 this.boxesEl = this.el.select('.mas-boxes').first();
28556 return this.boxesEl;
28560 initEvents : function()
28564 if(this.isAutoInitial){
28565 Roo.log('hook children rendered');
28566 this.on('childrenrendered', function() {
28567 Roo.log('children rendered');
28573 initial : function()
28575 this.currentSize = this.el.getBox(true);
28577 Roo.EventManager.onWindowResize(this.resize, this);
28579 if(!this.isAutoInitial){
28587 //this.layout.defer(500,this);
28591 resize : function()
28595 var cs = this.el.getBox(true);
28597 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28598 Roo.log("no change in with or X");
28602 this.currentSize = cs;
28608 layout : function()
28610 this._resetLayout();
28612 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28614 this.layoutItems( isInstant );
28616 this._isLayoutInited = true;
28620 _resetLayout : function()
28622 if(this.isHorizontal){
28623 this.horizontalMeasureColumns();
28627 this.verticalMeasureColumns();
28631 verticalMeasureColumns : function()
28633 this.getContainerWidth();
28635 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28636 // this.colWidth = Math.floor(this.containerWidth * 0.8);
28640 var boxWidth = this.boxWidth + this.padWidth;
28642 if(this.containerWidth < this.boxWidth){
28643 boxWidth = this.containerWidth
28646 var containerWidth = this.containerWidth;
28648 var cols = Math.floor(containerWidth / boxWidth);
28650 this.cols = Math.max( cols, 1 );
28652 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28654 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28656 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28658 this.colWidth = boxWidth + avail - this.padWidth;
28660 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28661 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
28664 horizontalMeasureColumns : function()
28666 this.getContainerWidth();
28668 var boxWidth = this.boxWidth;
28670 if(this.containerWidth < boxWidth){
28671 boxWidth = this.containerWidth;
28674 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28676 this.el.setHeight(boxWidth);
28680 getContainerWidth : function()
28682 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
28685 layoutItems : function( isInstant )
28687 var items = Roo.apply([], this.bricks);
28689 if(this.isHorizontal){
28690 this._horizontalLayoutItems( items , isInstant );
28694 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28695 // this._verticalAlternativeLayoutItems( items , isInstant );
28699 this._verticalLayoutItems( items , isInstant );
28703 _verticalLayoutItems : function ( items , isInstant)
28705 if ( !items || !items.length ) {
28710 ['xs', 'xs', 'xs', 'tall'],
28711 ['xs', 'xs', 'tall'],
28712 ['xs', 'xs', 'sm'],
28713 ['xs', 'xs', 'xs'],
28719 ['sm', 'xs', 'xs'],
28723 ['tall', 'xs', 'xs', 'xs'],
28724 ['tall', 'xs', 'xs'],
28736 Roo.each(items, function(item, k){
28738 switch (item.size) {
28739 // these layouts take up a full box,
28750 boxes.push([item]);
28773 var filterPattern = function(box, length)
28781 var pattern = box.slice(0, length);
28785 Roo.each(pattern, function(i){
28786 format.push(i.size);
28789 Roo.each(standard, function(s){
28791 if(String(s) != String(format)){
28800 if(!match && length == 1){
28805 filterPattern(box, length - 1);
28809 queue.push(pattern);
28811 box = box.slice(length, box.length);
28813 filterPattern(box, 4);
28819 Roo.each(boxes, function(box, k){
28825 if(box.length == 1){
28830 filterPattern(box, 4);
28834 this._processVerticalLayoutQueue( queue, isInstant );
28838 // _verticalAlternativeLayoutItems : function( items , isInstant )
28840 // if ( !items || !items.length ) {
28844 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
28848 _horizontalLayoutItems : function ( items , isInstant)
28850 if ( !items || !items.length || items.length < 3) {
28856 var eItems = items.slice(0, 3);
28858 items = items.slice(3, items.length);
28861 ['xs', 'xs', 'xs', 'wide'],
28862 ['xs', 'xs', 'wide'],
28863 ['xs', 'xs', 'sm'],
28864 ['xs', 'xs', 'xs'],
28870 ['sm', 'xs', 'xs'],
28874 ['wide', 'xs', 'xs', 'xs'],
28875 ['wide', 'xs', 'xs'],
28888 Roo.each(items, function(item, k){
28890 switch (item.size) {
28901 boxes.push([item]);
28925 var filterPattern = function(box, length)
28933 var pattern = box.slice(0, length);
28937 Roo.each(pattern, function(i){
28938 format.push(i.size);
28941 Roo.each(standard, function(s){
28943 if(String(s) != String(format)){
28952 if(!match && length == 1){
28957 filterPattern(box, length - 1);
28961 queue.push(pattern);
28963 box = box.slice(length, box.length);
28965 filterPattern(box, 4);
28971 Roo.each(boxes, function(box, k){
28977 if(box.length == 1){
28982 filterPattern(box, 4);
28989 var pos = this.el.getBox(true);
28993 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
28995 var hit_end = false;
28997 Roo.each(queue, function(box){
29001 Roo.each(box, function(b){
29003 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29013 Roo.each(box, function(b){
29015 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29018 mx = Math.max(mx, b.x);
29022 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29026 Roo.each(box, function(b){
29028 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29042 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29045 /** Sets position of item in DOM
29046 * @param {Element} item
29047 * @param {Number} x - horizontal position
29048 * @param {Number} y - vertical position
29049 * @param {Boolean} isInstant - disables transitions
29051 _processVerticalLayoutQueue : function( queue, isInstant )
29053 var pos = this.el.getBox(true);
29058 for (var i = 0; i < this.cols; i++){
29062 Roo.each(queue, function(box, k){
29064 var col = k % this.cols;
29066 Roo.each(box, function(b,kk){
29068 b.el.position('absolute');
29070 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29071 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29073 if(b.size == 'md-left' || b.size == 'md-right'){
29074 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29075 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29078 b.el.setWidth(width);
29079 b.el.setHeight(height);
29081 b.el.select('iframe',true).setSize(width,height);
29085 for (var i = 0; i < this.cols; i++){
29087 if(maxY[i] < maxY[col]){
29092 col = Math.min(col, i);
29096 x = pos.x + col * (this.colWidth + this.padWidth);
29100 var positions = [];
29102 switch (box.length){
29104 positions = this.getVerticalOneBoxColPositions(x, y, box);
29107 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29110 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29113 positions = this.getVerticalFourBoxColPositions(x, y, box);
29119 Roo.each(box, function(b,kk){
29121 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29123 var sz = b.el.getSize();
29125 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29133 for (var i = 0; i < this.cols; i++){
29134 mY = Math.max(mY, maxY[i]);
29137 this.el.setHeight(mY - pos.y);
29141 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29143 // var pos = this.el.getBox(true);
29146 // var maxX = pos.right;
29148 // var maxHeight = 0;
29150 // Roo.each(items, function(item, k){
29154 // item.el.position('absolute');
29156 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29158 // item.el.setWidth(width);
29160 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29162 // item.el.setHeight(height);
29165 // item.el.setXY([x, y], isInstant ? false : true);
29167 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29170 // y = y + height + this.alternativePadWidth;
29172 // maxHeight = maxHeight + height + this.alternativePadWidth;
29176 // this.el.setHeight(maxHeight);
29180 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29182 var pos = this.el.getBox(true);
29187 var maxX = pos.right;
29189 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29191 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29193 Roo.each(queue, function(box, k){
29195 Roo.each(box, function(b, kk){
29197 b.el.position('absolute');
29199 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29200 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29202 if(b.size == 'md-left' || b.size == 'md-right'){
29203 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29204 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29207 b.el.setWidth(width);
29208 b.el.setHeight(height);
29216 var positions = [];
29218 switch (box.length){
29220 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29223 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29226 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29229 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29235 Roo.each(box, function(b,kk){
29237 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29239 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29247 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29249 Roo.each(eItems, function(b,k){
29251 b.size = (k == 0) ? 'sm' : 'xs';
29252 b.x = (k == 0) ? 2 : 1;
29253 b.y = (k == 0) ? 2 : 1;
29255 b.el.position('absolute');
29257 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29259 b.el.setWidth(width);
29261 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29263 b.el.setHeight(height);
29267 var positions = [];
29270 x : maxX - this.unitWidth * 2 - this.gutter,
29275 x : maxX - this.unitWidth,
29276 y : minY + (this.unitWidth + this.gutter) * 2
29280 x : maxX - this.unitWidth * 3 - this.gutter * 2,
29284 Roo.each(eItems, function(b,k){
29286 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29292 getVerticalOneBoxColPositions : function(x, y, box)
29296 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29298 if(box[0].size == 'md-left'){
29302 if(box[0].size == 'md-right'){
29307 x : x + (this.unitWidth + this.gutter) * rand,
29314 getVerticalTwoBoxColPositions : function(x, y, box)
29318 if(box[0].size == 'xs'){
29322 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29326 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29340 x : x + (this.unitWidth + this.gutter) * 2,
29341 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29348 getVerticalThreeBoxColPositions : function(x, y, box)
29352 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29360 x : x + (this.unitWidth + this.gutter) * 1,
29365 x : x + (this.unitWidth + this.gutter) * 2,
29373 if(box[0].size == 'xs' && box[1].size == 'xs'){
29382 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29386 x : x + (this.unitWidth + this.gutter) * 1,
29400 x : x + (this.unitWidth + this.gutter) * 2,
29405 x : x + (this.unitWidth + this.gutter) * 2,
29406 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29413 getVerticalFourBoxColPositions : function(x, y, box)
29417 if(box[0].size == 'xs'){
29426 y : y + (this.unitHeight + this.gutter) * 1
29431 y : y + (this.unitHeight + this.gutter) * 2
29435 x : x + (this.unitWidth + this.gutter) * 1,
29449 x : x + (this.unitWidth + this.gutter) * 2,
29454 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29455 y : y + (this.unitHeight + this.gutter) * 1
29459 x : x + (this.unitWidth + this.gutter) * 2,
29460 y : y + (this.unitWidth + this.gutter) * 2
29467 getHorizontalOneBoxColPositions : function(maxX, minY, box)
29471 if(box[0].size == 'md-left'){
29473 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29480 if(box[0].size == 'md-right'){
29482 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29483 y : minY + (this.unitWidth + this.gutter) * 1
29489 var rand = Math.floor(Math.random() * (4 - box[0].y));
29492 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29493 y : minY + (this.unitWidth + this.gutter) * rand
29500 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29504 if(box[0].size == 'xs'){
29507 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29512 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29513 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29521 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29526 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29527 y : minY + (this.unitWidth + this.gutter) * 2
29534 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29538 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29541 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29546 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29547 y : minY + (this.unitWidth + this.gutter) * 1
29551 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29552 y : minY + (this.unitWidth + this.gutter) * 2
29559 if(box[0].size == 'xs' && box[1].size == 'xs'){
29562 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29567 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29572 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29573 y : minY + (this.unitWidth + this.gutter) * 1
29581 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29586 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29587 y : minY + (this.unitWidth + this.gutter) * 2
29591 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29592 y : minY + (this.unitWidth + this.gutter) * 2
29599 getHorizontalFourBoxColPositions : function(maxX, minY, box)
29603 if(box[0].size == 'xs'){
29606 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29611 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29616 x : maxX - this.unitWidth * box[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),
29621 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29622 y : minY + (this.unitWidth + this.gutter) * 1
29630 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29635 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29636 y : minY + (this.unitWidth + this.gutter) * 2
29640 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29641 y : minY + (this.unitWidth + this.gutter) * 2
29645 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),
29646 y : minY + (this.unitWidth + this.gutter) * 2
29660 * http://masonry.desandro.com
29662 * The idea is to render all the bricks based on vertical width...
29664 * The original code extends 'outlayer' - we might need to use that....
29670 * @class Roo.bootstrap.LayoutMasonryAuto
29671 * @extends Roo.bootstrap.Component
29672 * Bootstrap Layout Masonry class
29675 * Create a new Element
29676 * @param {Object} config The config object
29679 Roo.bootstrap.LayoutMasonryAuto = function(config){
29680 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29683 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
29686 * @cfg {Boolean} isFitWidth - resize the width..
29688 isFitWidth : false, // options..
29690 * @cfg {Boolean} isOriginLeft = left align?
29692 isOriginLeft : true,
29694 * @cfg {Boolean} isOriginTop = top align?
29696 isOriginTop : false,
29698 * @cfg {Boolean} isLayoutInstant = no animation?
29700 isLayoutInstant : false, // needed?
29702 * @cfg {Boolean} isResizingContainer = not sure if this is used..
29704 isResizingContainer : true,
29706 * @cfg {Number} columnWidth width of the columns
29712 * @cfg {Number} maxCols maximum number of columns
29717 * @cfg {Number} padHeight padding below box..
29723 * @cfg {Boolean} isAutoInitial defalut true
29726 isAutoInitial : true,
29732 initialColumnWidth : 0,
29733 currentSize : null,
29735 colYs : null, // array.
29742 bricks: null, //CompositeElement
29743 cols : 0, // array?
29744 // element : null, // wrapped now this.el
29745 _isLayoutInited : null,
29748 getAutoCreate : function(){
29752 cls: 'blog-masonary-wrapper ' + this.cls,
29754 cls : 'mas-boxes masonary'
29761 getChildContainer: function( )
29763 if (this.boxesEl) {
29764 return this.boxesEl;
29767 this.boxesEl = this.el.select('.mas-boxes').first();
29769 return this.boxesEl;
29773 initEvents : function()
29777 if(this.isAutoInitial){
29778 Roo.log('hook children rendered');
29779 this.on('childrenrendered', function() {
29780 Roo.log('children rendered');
29787 initial : function()
29789 this.reloadItems();
29791 this.currentSize = this.el.getBox(true);
29793 /// was window resize... - let's see if this works..
29794 Roo.EventManager.onWindowResize(this.resize, this);
29796 if(!this.isAutoInitial){
29801 this.layout.defer(500,this);
29804 reloadItems: function()
29806 this.bricks = this.el.select('.masonry-brick', true);
29808 this.bricks.each(function(b) {
29809 //Roo.log(b.getSize());
29810 if (!b.attr('originalwidth')) {
29811 b.attr('originalwidth', b.getSize().width);
29816 Roo.log(this.bricks.elements.length);
29819 resize : function()
29822 var cs = this.el.getBox(true);
29824 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29825 Roo.log("no change in with or X");
29828 this.currentSize = cs;
29832 layout : function()
29835 this._resetLayout();
29836 //this._manageStamps();
29838 // don't animate first layout
29839 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29840 this.layoutItems( isInstant );
29842 // flag for initalized
29843 this._isLayoutInited = true;
29846 layoutItems : function( isInstant )
29848 //var items = this._getItemsForLayout( this.items );
29849 // original code supports filtering layout items.. we just ignore it..
29851 this._layoutItems( this.bricks , isInstant );
29853 this._postLayout();
29855 _layoutItems : function ( items , isInstant)
29857 //this.fireEvent( 'layout', this, items );
29860 if ( !items || !items.elements.length ) {
29861 // no items, emit event with empty array
29866 items.each(function(item) {
29867 Roo.log("layout item");
29869 // get x/y object from method
29870 var position = this._getItemLayoutPosition( item );
29872 position.item = item;
29873 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29874 queue.push( position );
29877 this._processLayoutQueue( queue );
29879 /** Sets position of item in DOM
29880 * @param {Element} item
29881 * @param {Number} x - horizontal position
29882 * @param {Number} y - vertical position
29883 * @param {Boolean} isInstant - disables transitions
29885 _processLayoutQueue : function( queue )
29887 for ( var i=0, len = queue.length; i < len; i++ ) {
29888 var obj = queue[i];
29889 obj.item.position('absolute');
29890 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
29896 * Any logic you want to do after each layout,
29897 * i.e. size the container
29899 _postLayout : function()
29901 this.resizeContainer();
29904 resizeContainer : function()
29906 if ( !this.isResizingContainer ) {
29909 var size = this._getContainerSize();
29911 this.el.setSize(size.width,size.height);
29912 this.boxesEl.setSize(size.width,size.height);
29918 _resetLayout : function()
29920 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
29921 this.colWidth = this.el.getWidth();
29922 //this.gutter = this.el.getWidth();
29924 this.measureColumns();
29930 this.colYs.push( 0 );
29936 measureColumns : function()
29938 this.getContainerWidth();
29939 // if columnWidth is 0, default to outerWidth of first item
29940 if ( !this.columnWidth ) {
29941 var firstItem = this.bricks.first();
29942 Roo.log(firstItem);
29943 this.columnWidth = this.containerWidth;
29944 if (firstItem && firstItem.attr('originalwidth') ) {
29945 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
29947 // columnWidth fall back to item of first element
29948 Roo.log("set column width?");
29949 this.initialColumnWidth = this.columnWidth ;
29951 // if first elem has no width, default to size of container
29956 if (this.initialColumnWidth) {
29957 this.columnWidth = this.initialColumnWidth;
29962 // column width is fixed at the top - however if container width get's smaller we should
29965 // this bit calcs how man columns..
29967 var columnWidth = this.columnWidth += this.gutter;
29969 // calculate columns
29970 var containerWidth = this.containerWidth + this.gutter;
29972 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
29973 // fix rounding errors, typically with gutters
29974 var excess = columnWidth - containerWidth % columnWidth;
29977 // if overshoot is less than a pixel, round up, otherwise floor it
29978 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
29979 cols = Math[ mathMethod ]( cols );
29980 this.cols = Math.max( cols, 1 );
29981 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29983 // padding positioning..
29984 var totalColWidth = this.cols * this.columnWidth;
29985 var padavail = this.containerWidth - totalColWidth;
29986 // so for 2 columns - we need 3 'pads'
29988 var padNeeded = (1+this.cols) * this.padWidth;
29990 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
29992 this.columnWidth += padExtra
29993 //this.padWidth = Math.floor(padavail / ( this.cols));
29995 // adjust colum width so that padding is fixed??
29997 // we have 3 columns ... total = width * 3
29998 // we have X left over... that should be used by
30000 //if (this.expandC) {
30008 getContainerWidth : function()
30010 /* // container is parent if fit width
30011 var container = this.isFitWidth ? this.element.parentNode : this.element;
30012 // check that this.size and size are there
30013 // IE8 triggers resize on body size change, so they might not be
30015 var size = getSize( container ); //FIXME
30016 this.containerWidth = size && size.innerWidth; //FIXME
30019 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30023 _getItemLayoutPosition : function( item ) // what is item?
30025 // we resize the item to our columnWidth..
30027 item.setWidth(this.columnWidth);
30028 item.autoBoxAdjust = false;
30030 var sz = item.getSize();
30032 // how many columns does this brick span
30033 var remainder = this.containerWidth % this.columnWidth;
30035 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30036 // round if off by 1 pixel, otherwise use ceil
30037 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30038 colSpan = Math.min( colSpan, this.cols );
30040 // normally this should be '1' as we dont' currently allow multi width columns..
30042 var colGroup = this._getColGroup( colSpan );
30043 // get the minimum Y value from the columns
30044 var minimumY = Math.min.apply( Math, colGroup );
30045 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30047 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30049 // position the brick
30051 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30052 y: this.currentSize.y + minimumY + this.padHeight
30056 // apply setHeight to necessary columns
30057 var setHeight = minimumY + sz.height + this.padHeight;
30058 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30060 var setSpan = this.cols + 1 - colGroup.length;
30061 for ( var i = 0; i < setSpan; i++ ) {
30062 this.colYs[ shortColIndex + i ] = setHeight ;
30069 * @param {Number} colSpan - number of columns the element spans
30070 * @returns {Array} colGroup
30072 _getColGroup : function( colSpan )
30074 if ( colSpan < 2 ) {
30075 // if brick spans only one column, use all the column Ys
30080 // how many different places could this brick fit horizontally
30081 var groupCount = this.cols + 1 - colSpan;
30082 // for each group potential horizontal position
30083 for ( var i = 0; i < groupCount; i++ ) {
30084 // make an array of colY values for that one group
30085 var groupColYs = this.colYs.slice( i, i + colSpan );
30086 // and get the max value of the array
30087 colGroup[i] = Math.max.apply( Math, groupColYs );
30092 _manageStamp : function( stamp )
30094 var stampSize = stamp.getSize();
30095 var offset = stamp.getBox();
30096 // get the columns that this stamp affects
30097 var firstX = this.isOriginLeft ? offset.x : offset.right;
30098 var lastX = firstX + stampSize.width;
30099 var firstCol = Math.floor( firstX / this.columnWidth );
30100 firstCol = Math.max( 0, firstCol );
30102 var lastCol = Math.floor( lastX / this.columnWidth );
30103 // lastCol should not go over if multiple of columnWidth #425
30104 lastCol -= lastX % this.columnWidth ? 0 : 1;
30105 lastCol = Math.min( this.cols - 1, lastCol );
30107 // set colYs to bottom of the stamp
30108 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30111 for ( var i = firstCol; i <= lastCol; i++ ) {
30112 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30117 _getContainerSize : function()
30119 this.maxY = Math.max.apply( Math, this.colYs );
30124 if ( this.isFitWidth ) {
30125 size.width = this._getContainerFitWidth();
30131 _getContainerFitWidth : function()
30133 var unusedCols = 0;
30134 // count unused columns
30137 if ( this.colYs[i] !== 0 ) {
30142 // fit container to columns that have been used
30143 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30146 needsResizeLayout : function()
30148 var previousWidth = this.containerWidth;
30149 this.getContainerWidth();
30150 return previousWidth !== this.containerWidth;
30165 * @class Roo.bootstrap.MasonryBrick
30166 * @extends Roo.bootstrap.Component
30167 * Bootstrap MasonryBrick class
30170 * Create a new MasonryBrick
30171 * @param {Object} config The config object
30174 Roo.bootstrap.MasonryBrick = function(config){
30175 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30181 * When a MasonryBrick is clcik
30182 * @param {Roo.bootstrap.MasonryBrick} this
30183 * @param {Roo.EventObject} e
30189 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30192 * @cfg {String} title
30196 * @cfg {String} html
30200 * @cfg {String} bgimage
30204 * @cfg {String} videourl
30208 * @cfg {String} cls
30212 * @cfg {String} href
30216 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30221 * @cfg {String} (center|bottom) placetitle
30225 getAutoCreate : function()
30227 var cls = 'masonry-brick';
30229 if(this.href.length){
30230 cls += ' masonry-brick-link';
30233 if(this.bgimage.length){
30234 cls += ' masonry-brick-image';
30238 cls += ' masonry-' + this.size + '-brick';
30241 if(this.placetitle.length){
30243 switch (this.placetitle) {
30245 cls += ' masonry-center-title';
30248 cls += ' masonry-bottom-title';
30255 if(!this.html.length && !this.bgimage.length){
30256 cls += ' masonry-center-title';
30259 if(!this.html.length && this.bgimage.length){
30260 cls += ' masonry-bottom-title';
30265 cls += ' ' + this.cls;
30269 tag: (this.href.length) ? 'a' : 'div',
30274 cls: 'masonry-brick-paragraph',
30280 if(this.href.length){
30281 cfg.href = this.href;
30284 var cn = cfg.cn[0].cn;
30286 if(this.title.length){
30289 cls: 'masonry-brick-title',
30294 if(this.html.length){
30297 cls: 'masonry-brick-text',
30301 if (!this.title.length && !this.html.length) {
30302 cfg.cn[0].cls += ' hide';
30305 if(this.bgimage.length){
30308 cls: 'masonry-brick-image-view',
30312 if(this.videourl.length){
30313 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30314 // youtube support only?
30317 cls: 'masonry-brick-image-view',
30320 allowfullscreen : true
30329 initEvents: function()
30331 switch (this.size) {
30333 // this.intSize = 1;
30338 // this.intSize = 2;
30345 // this.intSize = 3;
30350 // this.intSize = 3;
30355 // this.intSize = 3;
30360 // this.intSize = 3;
30372 this.el.on('touchstart', this.onTouchStart, this);
30373 this.el.on('touchmove', this.onTouchMove, this);
30374 this.el.on('touchend', this.onTouchEnd, this);
30375 this.el.on('contextmenu', this.onContextMenu, this);
30377 this.el.on('mouseenter' ,this.enter, this);
30378 this.el.on('mouseleave', this.leave, this);
30381 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30382 this.parent().bricks.push(this);
30387 onClick: function(e, el)
30395 var time = this.endTimer - this.startTimer;
30403 e.preventDefault();
30406 enter: function(e, el)
30408 e.preventDefault();
30410 if(this.bgimage.length && this.html.length){
30411 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30415 leave: function(e, el)
30417 e.preventDefault();
30419 if(this.bgimage.length && this.html.length){
30420 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30424 onTouchStart: function(e, el)
30426 // e.preventDefault();
30428 if(!this.bgimage.length || !this.html.length){
30432 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30434 this.timer = new Date().getTime();
30436 this.touchmoved = false;
30439 onTouchMove: function(e, el)
30441 this.touchmoved = true;
30443 onContextMenu : function(e,el)
30445 e.preventDefault();
30446 e.stopPropagation();
30451 onTouchEnd: function(e, el)
30453 // e.preventDefault();
30455 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30462 if(!this.bgimage.length || !this.html.length){
30464 if(this.href.length){
30465 window.location.href = this.href;
30471 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30473 window.location.href = this.href;
30488 * @class Roo.bootstrap.Brick
30489 * @extends Roo.bootstrap.Component
30490 * Bootstrap Brick class
30493 * Create a new Brick
30494 * @param {Object} config The config object
30497 Roo.bootstrap.Brick = function(config){
30498 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30504 * When a Brick is click
30505 * @param {Roo.bootstrap.Brick} this
30506 * @param {Roo.EventObject} e
30512 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
30515 * @cfg {String} title
30519 * @cfg {String} html
30523 * @cfg {String} bgimage
30527 * @cfg {String} cls
30531 * @cfg {String} href
30535 * @cfg {String} video
30539 * @cfg {Boolean} square
30543 getAutoCreate : function()
30545 var cls = 'roo-brick';
30547 if(this.href.length){
30548 cls += ' roo-brick-link';
30551 if(this.bgimage.length){
30552 cls += ' roo-brick-image';
30555 if(!this.html.length && !this.bgimage.length){
30556 cls += ' roo-brick-center-title';
30559 if(!this.html.length && this.bgimage.length){
30560 cls += ' roo-brick-bottom-title';
30564 cls += ' ' + this.cls;
30568 tag: (this.href.length) ? 'a' : 'div',
30573 cls: 'roo-brick-paragraph',
30579 if(this.href.length){
30580 cfg.href = this.href;
30583 var cn = cfg.cn[0].cn;
30585 if(this.title.length){
30588 cls: 'roo-brick-title',
30593 if(this.html.length){
30596 cls: 'roo-brick-text',
30603 if(this.bgimage.length){
30606 cls: 'roo-brick-image-view',
30614 initEvents: function()
30616 if(this.title.length || this.html.length){
30617 this.el.on('mouseenter' ,this.enter, this);
30618 this.el.on('mouseleave', this.leave, this);
30622 Roo.EventManager.onWindowResize(this.resize, this);
30627 resize : function()
30629 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30631 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30632 // paragraph.setHeight(paragraph.getWidth());
30634 if(this.bgimage.length){
30635 var image = this.el.select('.roo-brick-image-view', true).first();
30636 image.setWidth(paragraph.getWidth());
30637 image.setHeight(paragraph.getWidth());
30642 enter: function(e, el)
30644 e.preventDefault();
30646 if(this.bgimage.length){
30647 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30648 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30652 leave: function(e, el)
30654 e.preventDefault();
30656 if(this.bgimage.length){
30657 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30658 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30668 * Ext JS Library 1.1.1
30669 * Copyright(c) 2006-2007, Ext JS, LLC.
30671 * Originally Released Under LGPL - original licence link has changed is not relivant.
30674 * <script type="text/javascript">
30679 * @class Roo.bootstrap.SplitBar
30680 * @extends Roo.util.Observable
30681 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30685 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30686 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30687 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30688 split.minSize = 100;
30689 split.maxSize = 600;
30690 split.animate = true;
30691 split.on('moved', splitterMoved);
30694 * Create a new SplitBar
30695 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
30696 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
30697 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30698 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
30699 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30700 position of the SplitBar).
30702 Roo.bootstrap.SplitBar = function(cfg){
30707 // dragElement : elm
30708 // resizingElement: el,
30710 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30711 // placement : Roo.bootstrap.SplitBar.LEFT ,
30712 // existingProxy ???
30715 this.el = Roo.get(cfg.dragElement, true);
30716 this.el.dom.unselectable = "on";
30718 this.resizingEl = Roo.get(cfg.resizingElement, true);
30722 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30723 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30726 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30729 * The minimum size of the resizing element. (Defaults to 0)
30735 * The maximum size of the resizing element. (Defaults to 2000)
30738 this.maxSize = 2000;
30741 * Whether to animate the transition to the new size
30744 this.animate = false;
30747 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30750 this.useShim = false;
30755 if(!cfg.existingProxy){
30757 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30759 this.proxy = Roo.get(cfg.existingProxy).dom;
30762 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30765 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30768 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30771 this.dragSpecs = {};
30774 * @private The adapter to use to positon and resize elements
30776 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30777 this.adapter.init(this);
30779 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30781 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30782 this.el.addClass("roo-splitbar-h");
30785 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30786 this.el.addClass("roo-splitbar-v");
30792 * Fires when the splitter is moved (alias for {@link #event-moved})
30793 * @param {Roo.bootstrap.SplitBar} this
30794 * @param {Number} newSize the new width or height
30799 * Fires when the splitter is moved
30800 * @param {Roo.bootstrap.SplitBar} this
30801 * @param {Number} newSize the new width or height
30805 * @event beforeresize
30806 * Fires before the splitter is dragged
30807 * @param {Roo.bootstrap.SplitBar} this
30809 "beforeresize" : true,
30811 "beforeapply" : true
30814 Roo.util.Observable.call(this);
30817 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30818 onStartProxyDrag : function(x, y){
30819 this.fireEvent("beforeresize", this);
30821 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
30823 o.enableDisplayMode("block");
30824 // all splitbars share the same overlay
30825 Roo.bootstrap.SplitBar.prototype.overlay = o;
30827 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30828 this.overlay.show();
30829 Roo.get(this.proxy).setDisplayed("block");
30830 var size = this.adapter.getElementSize(this);
30831 this.activeMinSize = this.getMinimumSize();;
30832 this.activeMaxSize = this.getMaximumSize();;
30833 var c1 = size - this.activeMinSize;
30834 var c2 = Math.max(this.activeMaxSize - size, 0);
30835 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30836 this.dd.resetConstraints();
30837 this.dd.setXConstraint(
30838 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
30839 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30841 this.dd.setYConstraint(0, 0);
30843 this.dd.resetConstraints();
30844 this.dd.setXConstraint(0, 0);
30845 this.dd.setYConstraint(
30846 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
30847 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30850 this.dragSpecs.startSize = size;
30851 this.dragSpecs.startPoint = [x, y];
30852 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30856 * @private Called after the drag operation by the DDProxy
30858 onEndProxyDrag : function(e){
30859 Roo.get(this.proxy).setDisplayed(false);
30860 var endPoint = Roo.lib.Event.getXY(e);
30862 this.overlay.hide();
30865 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30866 newSize = this.dragSpecs.startSize +
30867 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30868 endPoint[0] - this.dragSpecs.startPoint[0] :
30869 this.dragSpecs.startPoint[0] - endPoint[0]
30872 newSize = this.dragSpecs.startSize +
30873 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30874 endPoint[1] - this.dragSpecs.startPoint[1] :
30875 this.dragSpecs.startPoint[1] - endPoint[1]
30878 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30879 if(newSize != this.dragSpecs.startSize){
30880 if(this.fireEvent('beforeapply', this, newSize) !== false){
30881 this.adapter.setElementSize(this, newSize);
30882 this.fireEvent("moved", this, newSize);
30883 this.fireEvent("resize", this, newSize);
30889 * Get the adapter this SplitBar uses
30890 * @return The adapter object
30892 getAdapter : function(){
30893 return this.adapter;
30897 * Set the adapter this SplitBar uses
30898 * @param {Object} adapter A SplitBar adapter object
30900 setAdapter : function(adapter){
30901 this.adapter = adapter;
30902 this.adapter.init(this);
30906 * Gets the minimum size for the resizing element
30907 * @return {Number} The minimum size
30909 getMinimumSize : function(){
30910 return this.minSize;
30914 * Sets the minimum size for the resizing element
30915 * @param {Number} minSize The minimum size
30917 setMinimumSize : function(minSize){
30918 this.minSize = minSize;
30922 * Gets the maximum size for the resizing element
30923 * @return {Number} The maximum size
30925 getMaximumSize : function(){
30926 return this.maxSize;
30930 * Sets the maximum size for the resizing element
30931 * @param {Number} maxSize The maximum size
30933 setMaximumSize : function(maxSize){
30934 this.maxSize = maxSize;
30938 * Sets the initialize size for the resizing element
30939 * @param {Number} size The initial size
30941 setCurrentSize : function(size){
30942 var oldAnimate = this.animate;
30943 this.animate = false;
30944 this.adapter.setElementSize(this, size);
30945 this.animate = oldAnimate;
30949 * Destroy this splitbar.
30950 * @param {Boolean} removeEl True to remove the element
30952 destroy : function(removeEl){
30954 this.shim.remove();
30957 this.proxy.parentNode.removeChild(this.proxy);
30965 * @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.
30967 Roo.bootstrap.SplitBar.createProxy = function(dir){
30968 var proxy = new Roo.Element(document.createElement("div"));
30969 proxy.unselectable();
30970 var cls = 'roo-splitbar-proxy';
30971 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
30972 document.body.appendChild(proxy.dom);
30977 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
30978 * Default Adapter. It assumes the splitter and resizing element are not positioned
30979 * elements and only gets/sets the width of the element. Generally used for table based layouts.
30981 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
30984 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
30985 // do nothing for now
30986 init : function(s){
30990 * Called before drag operations to get the current size of the resizing element.
30991 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30993 getElementSize : function(s){
30994 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30995 return s.resizingEl.getWidth();
30997 return s.resizingEl.getHeight();
31002 * Called after drag operations to set the size of the resizing element.
31003 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31004 * @param {Number} newSize The new size to set
31005 * @param {Function} onComplete A function to be invoked when resizing is complete
31007 setElementSize : function(s, newSize, onComplete){
31008 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31010 s.resizingEl.setWidth(newSize);
31012 onComplete(s, newSize);
31015 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31020 s.resizingEl.setHeight(newSize);
31022 onComplete(s, newSize);
31025 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31032 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31033 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31034 * Adapter that moves the splitter element to align with the resized sizing element.
31035 * Used with an absolute positioned SplitBar.
31036 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31037 * document.body, make sure you assign an id to the body element.
31039 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31040 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31041 this.container = Roo.get(container);
31044 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31045 init : function(s){
31046 this.basic.init(s);
31049 getElementSize : function(s){
31050 return this.basic.getElementSize(s);
31053 setElementSize : function(s, newSize, onComplete){
31054 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31057 moveSplitter : function(s){
31058 var yes = Roo.bootstrap.SplitBar;
31059 switch(s.placement){
31061 s.el.setX(s.resizingEl.getRight());
31064 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31067 s.el.setY(s.resizingEl.getBottom());
31070 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31077 * Orientation constant - Create a vertical SplitBar
31081 Roo.bootstrap.SplitBar.VERTICAL = 1;
31084 * Orientation constant - Create a horizontal SplitBar
31088 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31091 * Placement constant - The resizing element is to the left of the splitter element
31095 Roo.bootstrap.SplitBar.LEFT = 1;
31098 * Placement constant - The resizing element is to the right of the splitter element
31102 Roo.bootstrap.SplitBar.RIGHT = 2;
31105 * Placement constant - The resizing element is positioned above the splitter element
31109 Roo.bootstrap.SplitBar.TOP = 3;
31112 * Placement constant - The resizing element is positioned under splitter element
31116 Roo.bootstrap.SplitBar.BOTTOM = 4;
31117 Roo.namespace("Roo.bootstrap.layout");/*
31119 * Ext JS Library 1.1.1
31120 * Copyright(c) 2006-2007, Ext JS, LLC.
31122 * Originally Released Under LGPL - original licence link has changed is not relivant.
31125 * <script type="text/javascript">
31129 * @class Roo.bootstrap.layout.Manager
31130 * @extends Roo.bootstrap.Component
31131 * Base class for layout managers.
31133 Roo.bootstrap.layout.Manager = function(config)
31135 Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31141 /** false to disable window resize monitoring @type Boolean */
31142 this.monitorWindowResize = true;
31147 * Fires when a layout is performed.
31148 * @param {Roo.LayoutManager} this
31152 * @event regionresized
31153 * Fires when the user resizes a region.
31154 * @param {Roo.LayoutRegion} region The resized region
31155 * @param {Number} newSize The new size (width for east/west, height for north/south)
31157 "regionresized" : true,
31159 * @event regioncollapsed
31160 * Fires when a region is collapsed.
31161 * @param {Roo.LayoutRegion} region The collapsed region
31163 "regioncollapsed" : true,
31165 * @event regionexpanded
31166 * Fires when a region is expanded.
31167 * @param {Roo.LayoutRegion} region The expanded region
31169 "regionexpanded" : true
31171 this.updating = false;
31174 this.el = Roo.get(config.el);
31180 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31185 monitorWindowResize : true,
31191 onRender : function(ct, position)
31194 this.el = Roo.get(ct);
31200 initEvents: function()
31204 // ie scrollbar fix
31205 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31206 document.body.scroll = "no";
31207 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31208 this.el.position('relative');
31210 this.id = this.el.id;
31211 this.el.addClass("roo-layout-container");
31212 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31213 if(this.el.dom != document.body ) {
31214 this.el.on('resize', this.layout,this);
31215 this.el.on('show', this.layout,this);
31221 * Returns true if this layout is currently being updated
31222 * @return {Boolean}
31224 isUpdating : function(){
31225 return this.updating;
31229 * Suspend the LayoutManager from doing auto-layouts while
31230 * making multiple add or remove calls
31232 beginUpdate : function(){
31233 this.updating = true;
31237 * Restore auto-layouts and optionally disable the manager from performing a layout
31238 * @param {Boolean} noLayout true to disable a layout update
31240 endUpdate : function(noLayout){
31241 this.updating = false;
31247 layout: function(){
31251 onRegionResized : function(region, newSize){
31252 this.fireEvent("regionresized", region, newSize);
31256 onRegionCollapsed : function(region){
31257 this.fireEvent("regioncollapsed", region);
31260 onRegionExpanded : function(region){
31261 this.fireEvent("regionexpanded", region);
31265 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31266 * performs box-model adjustments.
31267 * @return {Object} The size as an object {width: (the width), height: (the height)}
31269 getViewSize : function()
31272 if(this.el.dom != document.body){
31273 size = this.el.getSize();
31275 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31277 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31278 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31283 * Returns the Element this layout is bound to.
31284 * @return {Roo.Element}
31286 getEl : function(){
31291 * Returns the specified region.
31292 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31293 * @return {Roo.LayoutRegion}
31295 getRegion : function(target){
31296 return this.regions[target.toLowerCase()];
31299 onWindowResize : function(){
31300 if(this.monitorWindowResize){
31306 * Ext JS Library 1.1.1
31307 * Copyright(c) 2006-2007, Ext JS, LLC.
31309 * Originally Released Under LGPL - original licence link has changed is not relivant.
31312 * <script type="text/javascript">
31315 * @class Roo.bootstrap.layout.Border
31316 * @extends Roo.bootstrap.layout.Manager
31317 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31318 * please see: examples/bootstrap/nested.html<br><br>
31320 <b>The container the layout is rendered into can be either the body element or any other element.
31321 If it is not the body element, the container needs to either be an absolute positioned element,
31322 or you will need to add "position:relative" to the css of the container. You will also need to specify
31323 the container size if it is not the body element.</b>
31326 * Create a new Border
31327 * @param {Object} config Configuration options
31329 Roo.bootstrap.layout.Border = function(config){
31330 config = config || {};
31331 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31335 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31336 if(config[region]){
31337 config[region].region = region;
31338 this.addRegion(config[region]);
31344 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
31346 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31348 * Creates and adds a new region if it doesn't already exist.
31349 * @param {String} target The target region key (north, south, east, west or center).
31350 * @param {Object} config The regions config object
31351 * @return {BorderLayoutRegion} The new region
31353 addRegion : function(config)
31355 if(!this.regions[config.region]){
31356 var r = this.factory(config);
31357 this.bindRegion(r);
31359 return this.regions[config.region];
31363 bindRegion : function(r){
31364 this.regions[r.config.region] = r;
31366 r.on("visibilitychange", this.layout, this);
31367 r.on("paneladded", this.layout, this);
31368 r.on("panelremoved", this.layout, this);
31369 r.on("invalidated", this.layout, this);
31370 r.on("resized", this.onRegionResized, this);
31371 r.on("collapsed", this.onRegionCollapsed, this);
31372 r.on("expanded", this.onRegionExpanded, this);
31376 * Performs a layout update.
31378 layout : function()
31380 if(this.updating) {
31383 var size = this.getViewSize();
31384 var w = size.width;
31385 var h = size.height;
31390 //var x = 0, y = 0;
31392 var rs = this.regions;
31393 var north = rs["north"];
31394 var south = rs["south"];
31395 var west = rs["west"];
31396 var east = rs["east"];
31397 var center = rs["center"];
31398 //if(this.hideOnLayout){ // not supported anymore
31399 //c.el.setStyle("display", "none");
31401 if(north && north.isVisible()){
31402 var b = north.getBox();
31403 var m = north.getMargins();
31404 b.width = w - (m.left+m.right);
31407 centerY = b.height + b.y + m.bottom;
31408 centerH -= centerY;
31409 north.updateBox(this.safeBox(b));
31411 if(south && south.isVisible()){
31412 var b = south.getBox();
31413 var m = south.getMargins();
31414 b.width = w - (m.left+m.right);
31416 var totalHeight = (b.height + m.top + m.bottom);
31417 b.y = h - totalHeight + m.top;
31418 centerH -= totalHeight;
31419 south.updateBox(this.safeBox(b));
31421 if(west && west.isVisible()){
31422 var b = west.getBox();
31423 var m = west.getMargins();
31424 b.height = centerH - (m.top+m.bottom);
31426 b.y = centerY + m.top;
31427 var totalWidth = (b.width + m.left + m.right);
31428 centerX += totalWidth;
31429 centerW -= totalWidth;
31430 west.updateBox(this.safeBox(b));
31432 if(east && east.isVisible()){
31433 var b = east.getBox();
31434 var m = east.getMargins();
31435 b.height = centerH - (m.top+m.bottom);
31436 var totalWidth = (b.width + m.left + m.right);
31437 b.x = w - totalWidth + m.left;
31438 b.y = centerY + m.top;
31439 centerW -= totalWidth;
31440 east.updateBox(this.safeBox(b));
31443 var m = center.getMargins();
31445 x: centerX + m.left,
31446 y: centerY + m.top,
31447 width: centerW - (m.left+m.right),
31448 height: centerH - (m.top+m.bottom)
31450 //if(this.hideOnLayout){
31451 //center.el.setStyle("display", "block");
31453 center.updateBox(this.safeBox(centerBox));
31456 this.fireEvent("layout", this);
31460 safeBox : function(box){
31461 box.width = Math.max(0, box.width);
31462 box.height = Math.max(0, box.height);
31467 * Adds a ContentPanel (or subclass) to this layout.
31468 * @param {String} target The target region key (north, south, east, west or center).
31469 * @param {Roo.ContentPanel} panel The panel to add
31470 * @return {Roo.ContentPanel} The added panel
31472 add : function(target, panel){
31474 target = target.toLowerCase();
31475 return this.regions[target].add(panel);
31479 * Remove a ContentPanel (or subclass) to this layout.
31480 * @param {String} target The target region key (north, south, east, west or center).
31481 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31482 * @return {Roo.ContentPanel} The removed panel
31484 remove : function(target, panel){
31485 target = target.toLowerCase();
31486 return this.regions[target].remove(panel);
31490 * Searches all regions for a panel with the specified id
31491 * @param {String} panelId
31492 * @return {Roo.ContentPanel} The panel or null if it wasn't found
31494 findPanel : function(panelId){
31495 var rs = this.regions;
31496 for(var target in rs){
31497 if(typeof rs[target] != "function"){
31498 var p = rs[target].getPanel(panelId);
31508 * Searches all regions for a panel with the specified id and activates (shows) it.
31509 * @param {String/ContentPanel} panelId The panels id or the panel itself
31510 * @return {Roo.ContentPanel} The shown panel or null
31512 showPanel : function(panelId) {
31513 var rs = this.regions;
31514 for(var target in rs){
31515 var r = rs[target];
31516 if(typeof r != "function"){
31517 if(r.hasPanel(panelId)){
31518 return r.showPanel(panelId);
31526 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31527 * @param {Roo.state.Provider} provider (optional) An alternate state provider
31530 restoreState : function(provider){
31532 provider = Roo.state.Manager;
31534 var sm = new Roo.LayoutStateManager();
31535 sm.init(this, provider);
31541 * Adds a xtype elements to the layout.
31545 xtype : 'ContentPanel',
31552 xtype : 'NestedLayoutPanel',
31558 items : [ ... list of content panels or nested layout panels.. ]
31562 * @param {Object} cfg Xtype definition of item to add.
31564 addxtype : function(cfg)
31566 // basically accepts a pannel...
31567 // can accept a layout region..!?!?
31568 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31571 // theory? children can only be panels??
31573 //if (!cfg.xtype.match(/Panel$/)) {
31578 if (typeof(cfg.region) == 'undefined') {
31579 Roo.log("Failed to add Panel, region was not set");
31583 var region = cfg.region;
31589 xitems = cfg.items;
31596 case 'Content': // ContentPanel (el, cfg)
31597 case 'Scroll': // ContentPanel (el, cfg)
31599 cfg.autoCreate = true;
31600 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31602 // var el = this.el.createChild();
31603 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31606 this.add(region, ret);
31610 case 'TreePanel': // our new panel!
31611 cfg.el = this.el.createChild();
31612 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31613 this.add(region, ret);
31618 // create a new Layout (which is a Border Layout...
31620 var clayout = cfg.layout;
31621 clayout.el = this.el.createChild();
31622 clayout.items = clayout.items || [];
31626 // replace this exitems with the clayout ones..
31627 xitems = clayout.items;
31629 // force background off if it's in center...
31630 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31631 cfg.background = false;
31633 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
31636 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31637 //console.log('adding nested layout panel ' + cfg.toSource());
31638 this.add(region, ret);
31639 nb = {}; /// find first...
31644 // needs grid and region
31646 //var el = this.getRegion(region).el.createChild();
31648 *var el = this.el.createChild();
31649 // create the grid first...
31650 cfg.grid.container = el;
31651 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31654 if (region == 'center' && this.active ) {
31655 cfg.background = false;
31658 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31660 this.add(region, ret);
31662 if (cfg.background) {
31663 // render grid on panel activation (if panel background)
31664 ret.on('activate', function(gp) {
31665 if (!gp.grid.rendered) {
31666 // gp.grid.render(el);
31670 // cfg.grid.render(el);
31676 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31677 // it was the old xcomponent building that caused this before.
31678 // espeically if border is the top element in the tree.
31688 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31690 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31691 this.add(region, ret);
31695 throw "Can not add '" + cfg.xtype + "' to Border";
31701 this.beginUpdate();
31705 Roo.each(xitems, function(i) {
31706 region = nb && i.region ? i.region : false;
31708 var add = ret.addxtype(i);
31711 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31712 if (!i.background) {
31713 abn[region] = nb[region] ;
31720 // make the last non-background panel active..
31721 //if (nb) { Roo.log(abn); }
31724 for(var r in abn) {
31725 region = this.getRegion(r);
31727 // tried using nb[r], but it does not work..
31729 region.showPanel(abn[r]);
31740 factory : function(cfg)
31743 var validRegions = Roo.bootstrap.layout.Border.regions;
31745 var target = cfg.region;
31748 var r = Roo.bootstrap.layout;
31752 return new r.North(cfg);
31754 return new r.South(cfg);
31756 return new r.East(cfg);
31758 return new r.West(cfg);
31760 return new r.Center(cfg);
31762 throw 'Layout region "'+target+'" not supported.';
31769 * Ext JS Library 1.1.1
31770 * Copyright(c) 2006-2007, Ext JS, LLC.
31772 * Originally Released Under LGPL - original licence link has changed is not relivant.
31775 * <script type="text/javascript">
31779 * @class Roo.bootstrap.layout.Basic
31780 * @extends Roo.util.Observable
31781 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31782 * and does not have a titlebar, tabs or any other features. All it does is size and position
31783 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31784 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
31785 * @cfg {string} region the region that it inhabits..
31786 * @cfg {bool} skipConfig skip config?
31790 Roo.bootstrap.layout.Basic = function(config){
31792 this.mgr = config.mgr;
31794 this.position = config.region;
31796 var skipConfig = config.skipConfig;
31800 * @scope Roo.BasicLayoutRegion
31804 * @event beforeremove
31805 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31806 * @param {Roo.LayoutRegion} this
31807 * @param {Roo.ContentPanel} panel The panel
31808 * @param {Object} e The cancel event object
31810 "beforeremove" : true,
31812 * @event invalidated
31813 * Fires when the layout for this region is changed.
31814 * @param {Roo.LayoutRegion} this
31816 "invalidated" : true,
31818 * @event visibilitychange
31819 * Fires when this region is shown or hidden
31820 * @param {Roo.LayoutRegion} this
31821 * @param {Boolean} visibility true or false
31823 "visibilitychange" : true,
31825 * @event paneladded
31826 * Fires when a panel is added.
31827 * @param {Roo.LayoutRegion} this
31828 * @param {Roo.ContentPanel} panel The panel
31830 "paneladded" : true,
31832 * @event panelremoved
31833 * Fires when a panel is removed.
31834 * @param {Roo.LayoutRegion} this
31835 * @param {Roo.ContentPanel} panel The panel
31837 "panelremoved" : true,
31839 * @event beforecollapse
31840 * Fires when this region before collapse.
31841 * @param {Roo.LayoutRegion} this
31843 "beforecollapse" : true,
31846 * Fires when this region is collapsed.
31847 * @param {Roo.LayoutRegion} this
31849 "collapsed" : true,
31852 * Fires when this region is expanded.
31853 * @param {Roo.LayoutRegion} this
31858 * Fires when this region is slid into view.
31859 * @param {Roo.LayoutRegion} this
31861 "slideshow" : true,
31864 * Fires when this region slides out of view.
31865 * @param {Roo.LayoutRegion} this
31867 "slidehide" : true,
31869 * @event panelactivated
31870 * Fires when a panel is activated.
31871 * @param {Roo.LayoutRegion} this
31872 * @param {Roo.ContentPanel} panel The activated panel
31874 "panelactivated" : true,
31877 * Fires when the user resizes this region.
31878 * @param {Roo.LayoutRegion} this
31879 * @param {Number} newSize The new size (width for east/west, height for north/south)
31883 /** A collection of panels in this region. @type Roo.util.MixedCollection */
31884 this.panels = new Roo.util.MixedCollection();
31885 this.panels.getKey = this.getPanelId.createDelegate(this);
31887 this.activePanel = null;
31888 // ensure listeners are added...
31890 if (config.listeners || config.events) {
31891 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
31892 listeners : config.listeners || {},
31893 events : config.events || {}
31897 if(skipConfig !== true){
31898 this.applyConfig(config);
31902 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
31904 getPanelId : function(p){
31908 applyConfig : function(config){
31909 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31910 this.config = config;
31915 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
31916 * the width, for horizontal (north, south) the height.
31917 * @param {Number} newSize The new width or height
31919 resizeTo : function(newSize){
31920 var el = this.el ? this.el :
31921 (this.activePanel ? this.activePanel.getEl() : null);
31923 switch(this.position){
31926 el.setWidth(newSize);
31927 this.fireEvent("resized", this, newSize);
31931 el.setHeight(newSize);
31932 this.fireEvent("resized", this, newSize);
31938 getBox : function(){
31939 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31942 getMargins : function(){
31943 return this.margins;
31946 updateBox : function(box){
31948 var el = this.activePanel.getEl();
31949 el.dom.style.left = box.x + "px";
31950 el.dom.style.top = box.y + "px";
31951 this.activePanel.setSize(box.width, box.height);
31955 * Returns the container element for this region.
31956 * @return {Roo.Element}
31958 getEl : function(){
31959 return this.activePanel;
31963 * Returns true if this region is currently visible.
31964 * @return {Boolean}
31966 isVisible : function(){
31967 return this.activePanel ? true : false;
31970 setActivePanel : function(panel){
31971 panel = this.getPanel(panel);
31972 if(this.activePanel && this.activePanel != panel){
31973 this.activePanel.setActiveState(false);
31974 this.activePanel.getEl().setLeftTop(-10000,-10000);
31976 this.activePanel = panel;
31977 panel.setActiveState(true);
31979 panel.setSize(this.box.width, this.box.height);
31981 this.fireEvent("panelactivated", this, panel);
31982 this.fireEvent("invalidated");
31986 * Show the specified panel.
31987 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31988 * @return {Roo.ContentPanel} The shown panel or null
31990 showPanel : function(panel){
31991 panel = this.getPanel(panel);
31993 this.setActivePanel(panel);
31999 * Get the active panel for this region.
32000 * @return {Roo.ContentPanel} The active panel or null
32002 getActivePanel : function(){
32003 return this.activePanel;
32007 * Add the passed ContentPanel(s)
32008 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32009 * @return {Roo.ContentPanel} The panel added (if only one was added)
32011 add : function(panel){
32012 if(arguments.length > 1){
32013 for(var i = 0, len = arguments.length; i < len; i++) {
32014 this.add(arguments[i]);
32018 if(this.hasPanel(panel)){
32019 this.showPanel(panel);
32022 var el = panel.getEl();
32023 if(el.dom.parentNode != this.mgr.el.dom){
32024 this.mgr.el.dom.appendChild(el.dom);
32026 if(panel.setRegion){
32027 panel.setRegion(this);
32029 this.panels.add(panel);
32030 el.setStyle("position", "absolute");
32031 if(!panel.background){
32032 this.setActivePanel(panel);
32033 if(this.config.initialSize && this.panels.getCount()==1){
32034 this.resizeTo(this.config.initialSize);
32037 this.fireEvent("paneladded", this, panel);
32042 * Returns true if the panel is in this region.
32043 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32044 * @return {Boolean}
32046 hasPanel : function(panel){
32047 if(typeof panel == "object"){ // must be panel obj
32048 panel = panel.getId();
32050 return this.getPanel(panel) ? true : false;
32054 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32055 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32056 * @param {Boolean} preservePanel Overrides the config preservePanel option
32057 * @return {Roo.ContentPanel} The panel that was removed
32059 remove : function(panel, preservePanel){
32060 panel = this.getPanel(panel);
32065 this.fireEvent("beforeremove", this, panel, e);
32066 if(e.cancel === true){
32069 var panelId = panel.getId();
32070 this.panels.removeKey(panelId);
32075 * Returns the panel specified or null if it's not in this region.
32076 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32077 * @return {Roo.ContentPanel}
32079 getPanel : function(id){
32080 if(typeof id == "object"){ // must be panel obj
32083 return this.panels.get(id);
32087 * Returns this regions position (north/south/east/west/center).
32090 getPosition: function(){
32091 return this.position;
32095 * Ext JS Library 1.1.1
32096 * Copyright(c) 2006-2007, Ext JS, LLC.
32098 * Originally Released Under LGPL - original licence link has changed is not relivant.
32101 * <script type="text/javascript">
32105 * @class Roo.bootstrap.layout.Region
32106 * @extends Roo.bootstrap.layout.Basic
32107 * This class represents a region in a layout manager.
32109 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32110 * @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})
32111 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
32112 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
32113 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
32114 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
32115 * @cfg {String} title The title for the region (overrides panel titles)
32116 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
32117 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32118 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
32119 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32120 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
32121 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32122 * the space available, similar to FireFox 1.5 tabs (defaults to false)
32123 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
32124 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
32125 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
32127 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
32128 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
32129 * @cfg {Boolean} disableTabTips True to disable tab tooltips
32130 * @cfg {Number} width For East/West panels
32131 * @cfg {Number} height For North/South panels
32132 * @cfg {Boolean} split To show the splitter
32133 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
32135 * @cfg {string} cls Extra CSS classes to add to region
32137 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32138 * @cfg {string} region the region that it inhabits..
32141 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
32142 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
32144 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
32145 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
32146 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
32148 Roo.bootstrap.layout.Region = function(config)
32150 this.applyConfig(config);
32152 var mgr = config.mgr;
32153 var pos = config.region;
32154 config.skipConfig = true;
32155 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32158 this.onRender(mgr.el);
32161 this.visible = true;
32162 this.collapsed = false;
32165 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32167 position: '', // set by wrapper (eg. north/south etc..)
32169 createBody : function(){
32170 /** This region's body element
32171 * @type Roo.Element */
32172 this.bodyEl = this.el.createChild({
32174 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32178 onRender: function(ctr, pos)
32180 var dh = Roo.DomHelper;
32181 /** This region's container element
32182 * @type Roo.Element */
32183 this.el = dh.append(ctr.dom, {
32185 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32187 /** This region's title element
32188 * @type Roo.Element */
32190 this.titleEl = dh.append(this.el.dom,
32193 unselectable: "on",
32194 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32196 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
32197 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32200 this.titleEl.enableDisplayMode();
32201 /** This region's title text element
32202 * @type HTMLElement */
32203 this.titleTextEl = this.titleEl.dom.firstChild;
32204 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32206 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32207 this.closeBtn.enableDisplayMode();
32208 this.closeBtn.on("click", this.closeClicked, this);
32209 this.closeBtn.hide();
32211 this.createBody(this.config);
32212 if(this.config.hideWhenEmpty){
32214 this.on("paneladded", this.validateVisibility, this);
32215 this.on("panelremoved", this.validateVisibility, this);
32217 if(this.autoScroll){
32218 this.bodyEl.setStyle("overflow", "auto");
32220 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32222 //if(c.titlebar !== false){
32223 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32224 this.titleEl.hide();
32226 this.titleEl.show();
32227 if(this.config.title){
32228 this.titleTextEl.innerHTML = this.config.title;
32232 if(this.config.collapsed){
32233 this.collapse(true);
32235 if(this.config.hidden){
32240 applyConfig : function(c)
32243 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32244 var dh = Roo.DomHelper;
32245 if(c.titlebar !== false){
32246 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32247 this.collapseBtn.on("click", this.collapse, this);
32248 this.collapseBtn.enableDisplayMode();
32250 if(c.showPin === true || this.showPin){
32251 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32252 this.stickBtn.enableDisplayMode();
32253 this.stickBtn.on("click", this.expand, this);
32254 this.stickBtn.hide();
32259 /** This region's collapsed element
32260 * @type Roo.Element */
32263 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32264 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32267 if(c.floatable !== false){
32268 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32269 this.collapsedEl.on("click", this.collapseClick, this);
32272 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32273 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32274 id: "message", unselectable: "on", style:{"float":"left"}});
32275 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32277 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32278 this.expandBtn.on("click", this.expand, this);
32282 if(this.collapseBtn){
32283 this.collapseBtn.setVisible(c.collapsible == true);
32286 this.cmargins = c.cmargins || this.cmargins ||
32287 (this.position == "west" || this.position == "east" ?
32288 {top: 0, left: 2, right:2, bottom: 0} :
32289 {top: 2, left: 0, right:0, bottom: 2});
32291 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32294 this.bottomTabs = c.tabPosition != "top";
32296 this.autoScroll = c.autoScroll || false;
32301 this.duration = c.duration || .30;
32302 this.slideDuration = c.slideDuration || .45;
32307 * Returns true if this region is currently visible.
32308 * @return {Boolean}
32310 isVisible : function(){
32311 return this.visible;
32315 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32316 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
32318 //setCollapsedTitle : function(title){
32319 // title = title || " ";
32320 // if(this.collapsedTitleTextEl){
32321 // this.collapsedTitleTextEl.innerHTML = title;
32325 getBox : function(){
32327 // if(!this.collapsed){
32328 b = this.el.getBox(false, true);
32330 // b = this.collapsedEl.getBox(false, true);
32335 getMargins : function(){
32336 return this.margins;
32337 //return this.collapsed ? this.cmargins : this.margins;
32340 highlight : function(){
32341 this.el.addClass("x-layout-panel-dragover");
32344 unhighlight : function(){
32345 this.el.removeClass("x-layout-panel-dragover");
32348 updateBox : function(box)
32351 if(!this.collapsed){
32352 this.el.dom.style.left = box.x + "px";
32353 this.el.dom.style.top = box.y + "px";
32354 this.updateBody(box.width, box.height);
32356 this.collapsedEl.dom.style.left = box.x + "px";
32357 this.collapsedEl.dom.style.top = box.y + "px";
32358 this.collapsedEl.setSize(box.width, box.height);
32361 this.tabs.autoSizeTabs();
32365 updateBody : function(w, h)
32368 this.el.setWidth(w);
32369 w -= this.el.getBorderWidth("rl");
32370 if(this.config.adjustments){
32371 w += this.config.adjustments[0];
32375 this.el.setHeight(h);
32376 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32377 h -= this.el.getBorderWidth("tb");
32378 if(this.config.adjustments){
32379 h += this.config.adjustments[1];
32381 this.bodyEl.setHeight(h);
32383 h = this.tabs.syncHeight(h);
32386 if(this.panelSize){
32387 w = w !== null ? w : this.panelSize.width;
32388 h = h !== null ? h : this.panelSize.height;
32390 if(this.activePanel){
32391 var el = this.activePanel.getEl();
32392 w = w !== null ? w : el.getWidth();
32393 h = h !== null ? h : el.getHeight();
32394 this.panelSize = {width: w, height: h};
32395 this.activePanel.setSize(w, h);
32397 if(Roo.isIE && this.tabs){
32398 this.tabs.el.repaint();
32403 * Returns the container element for this region.
32404 * @return {Roo.Element}
32406 getEl : function(){
32411 * Hides this region.
32414 //if(!this.collapsed){
32415 this.el.dom.style.left = "-2000px";
32418 // this.collapsedEl.dom.style.left = "-2000px";
32419 // this.collapsedEl.hide();
32421 this.visible = false;
32422 this.fireEvent("visibilitychange", this, false);
32426 * Shows this region if it was previously hidden.
32429 //if(!this.collapsed){
32432 // this.collapsedEl.show();
32434 this.visible = true;
32435 this.fireEvent("visibilitychange", this, true);
32438 closeClicked : function(){
32439 if(this.activePanel){
32440 this.remove(this.activePanel);
32444 collapseClick : function(e){
32446 e.stopPropagation();
32449 e.stopPropagation();
32455 * Collapses this region.
32456 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32459 collapse : function(skipAnim, skipCheck = false){
32460 if(this.collapsed) {
32464 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32466 this.collapsed = true;
32468 this.split.el.hide();
32470 if(this.config.animate && skipAnim !== true){
32471 this.fireEvent("invalidated", this);
32472 this.animateCollapse();
32474 this.el.setLocation(-20000,-20000);
32476 this.collapsedEl.show();
32477 this.fireEvent("collapsed", this);
32478 this.fireEvent("invalidated", this);
32484 animateCollapse : function(){
32489 * Expands this region if it was previously collapsed.
32490 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32491 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32494 expand : function(e, skipAnim){
32496 e.stopPropagation();
32498 if(!this.collapsed || this.el.hasActiveFx()) {
32502 this.afterSlideIn();
32505 this.collapsed = false;
32506 if(this.config.animate && skipAnim !== true){
32507 this.animateExpand();
32511 this.split.el.show();
32513 this.collapsedEl.setLocation(-2000,-2000);
32514 this.collapsedEl.hide();
32515 this.fireEvent("invalidated", this);
32516 this.fireEvent("expanded", this);
32520 animateExpand : function(){
32524 initTabs : function()
32526 this.bodyEl.setStyle("overflow", "hidden");
32527 var ts = new Roo.bootstrap.panel.Tabs({
32528 el: this.bodyEl.dom,
32529 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32530 disableTooltips: this.config.disableTabTips,
32531 toolbar : this.config.toolbar
32534 if(this.config.hideTabs){
32535 ts.stripWrap.setDisplayed(false);
32538 ts.resizeTabs = this.config.resizeTabs === true;
32539 ts.minTabWidth = this.config.minTabWidth || 40;
32540 ts.maxTabWidth = this.config.maxTabWidth || 250;
32541 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32542 ts.monitorResize = false;
32543 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32544 ts.bodyEl.addClass('roo-layout-tabs-body');
32545 this.panels.each(this.initPanelAsTab, this);
32548 initPanelAsTab : function(panel){
32549 var ti = this.tabs.addTab(
32551 panel.getTitle(), null,
32552 this.config.closeOnTab && panel.isClosable()
32554 if(panel.tabTip !== undefined){
32555 ti.setTooltip(panel.tabTip);
32557 ti.on("activate", function(){
32558 this.setActivePanel(panel);
32561 if(this.config.closeOnTab){
32562 ti.on("beforeclose", function(t, e){
32564 this.remove(panel);
32570 updatePanelTitle : function(panel, title)
32572 if(this.activePanel == panel){
32573 this.updateTitle(title);
32576 var ti = this.tabs.getTab(panel.getEl().id);
32578 if(panel.tabTip !== undefined){
32579 ti.setTooltip(panel.tabTip);
32584 updateTitle : function(title){
32585 if(this.titleTextEl && !this.config.title){
32586 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
32590 setActivePanel : function(panel)
32592 panel = this.getPanel(panel);
32593 if(this.activePanel && this.activePanel != panel){
32594 this.activePanel.setActiveState(false);
32596 this.activePanel = panel;
32597 panel.setActiveState(true);
32598 if(this.panelSize){
32599 panel.setSize(this.panelSize.width, this.panelSize.height);
32602 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32604 this.updateTitle(panel.getTitle());
32606 this.fireEvent("invalidated", this);
32608 this.fireEvent("panelactivated", this, panel);
32612 * Shows the specified panel.
32613 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32614 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32616 showPanel : function(panel)
32618 panel = this.getPanel(panel);
32621 var tab = this.tabs.getTab(panel.getEl().id);
32622 if(tab.isHidden()){
32623 this.tabs.unhideTab(tab.id);
32627 this.setActivePanel(panel);
32634 * Get the active panel for this region.
32635 * @return {Roo.ContentPanel} The active panel or null
32637 getActivePanel : function(){
32638 return this.activePanel;
32641 validateVisibility : function(){
32642 if(this.panels.getCount() < 1){
32643 this.updateTitle(" ");
32644 this.closeBtn.hide();
32647 if(!this.isVisible()){
32654 * Adds the passed ContentPanel(s) to this region.
32655 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32656 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32658 add : function(panel){
32659 if(arguments.length > 1){
32660 for(var i = 0, len = arguments.length; i < len; i++) {
32661 this.add(arguments[i]);
32665 if(this.hasPanel(panel)){
32666 this.showPanel(panel);
32669 panel.setRegion(this);
32670 this.panels.add(panel);
32671 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32672 this.bodyEl.dom.appendChild(panel.getEl().dom);
32673 if(panel.background !== true){
32674 this.setActivePanel(panel);
32676 this.fireEvent("paneladded", this, panel);
32682 this.initPanelAsTab(panel);
32686 if(panel.background !== true){
32687 this.tabs.activate(panel.getEl().id);
32689 this.fireEvent("paneladded", this, panel);
32694 * Hides the tab for the specified panel.
32695 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32697 hidePanel : function(panel){
32698 if(this.tabs && (panel = this.getPanel(panel))){
32699 this.tabs.hideTab(panel.getEl().id);
32704 * Unhides the tab for a previously hidden panel.
32705 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32707 unhidePanel : function(panel){
32708 if(this.tabs && (panel = this.getPanel(panel))){
32709 this.tabs.unhideTab(panel.getEl().id);
32713 clearPanels : function(){
32714 while(this.panels.getCount() > 0){
32715 this.remove(this.panels.first());
32720 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32721 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32722 * @param {Boolean} preservePanel Overrides the config preservePanel option
32723 * @return {Roo.ContentPanel} The panel that was removed
32725 remove : function(panel, preservePanel)
32727 panel = this.getPanel(panel);
32732 this.fireEvent("beforeremove", this, panel, e);
32733 if(e.cancel === true){
32736 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32737 var panelId = panel.getId();
32738 this.panels.removeKey(panelId);
32740 document.body.appendChild(panel.getEl().dom);
32743 this.tabs.removeTab(panel.getEl().id);
32744 }else if (!preservePanel){
32745 this.bodyEl.dom.removeChild(panel.getEl().dom);
32747 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32748 var p = this.panels.first();
32749 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32750 tempEl.appendChild(p.getEl().dom);
32751 this.bodyEl.update("");
32752 this.bodyEl.dom.appendChild(p.getEl().dom);
32754 this.updateTitle(p.getTitle());
32756 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32757 this.setActivePanel(p);
32759 panel.setRegion(null);
32760 if(this.activePanel == panel){
32761 this.activePanel = null;
32763 if(this.config.autoDestroy !== false && preservePanel !== true){
32764 try{panel.destroy();}catch(e){}
32766 this.fireEvent("panelremoved", this, panel);
32771 * Returns the TabPanel component used by this region
32772 * @return {Roo.TabPanel}
32774 getTabs : function(){
32778 createTool : function(parentEl, className){
32779 var btn = Roo.DomHelper.append(parentEl, {
32781 cls: "x-layout-tools-button",
32784 cls: "roo-layout-tools-button-inner " + className,
32788 btn.addClassOnOver("roo-layout-tools-button-over");
32793 * Ext JS Library 1.1.1
32794 * Copyright(c) 2006-2007, Ext JS, LLC.
32796 * Originally Released Under LGPL - original licence link has changed is not relivant.
32799 * <script type="text/javascript">
32805 * @class Roo.SplitLayoutRegion
32806 * @extends Roo.LayoutRegion
32807 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32809 Roo.bootstrap.layout.Split = function(config){
32810 this.cursor = config.cursor;
32811 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32814 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32816 splitTip : "Drag to resize.",
32817 collapsibleSplitTip : "Drag to resize. Double click to hide.",
32818 useSplitTips : false,
32820 applyConfig : function(config){
32821 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32824 onRender : function(ctr,pos) {
32826 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
32827 if(!this.config.split){
32832 var splitEl = Roo.DomHelper.append(ctr.dom, {
32834 id: this.el.id + "-split",
32835 cls: "roo-layout-split roo-layout-split-"+this.position,
32838 /** The SplitBar for this region
32839 * @type Roo.SplitBar */
32840 // does not exist yet...
32841 Roo.log([this.position, this.orientation]);
32843 this.split = new Roo.bootstrap.SplitBar({
32844 dragElement : splitEl,
32845 resizingElement: this.el,
32846 orientation : this.orientation
32849 this.split.on("moved", this.onSplitMove, this);
32850 this.split.useShim = this.config.useShim === true;
32851 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32852 if(this.useSplitTips){
32853 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32855 //if(config.collapsible){
32856 // this.split.el.on("dblclick", this.collapse, this);
32859 if(typeof this.config.minSize != "undefined"){
32860 this.split.minSize = this.config.minSize;
32862 if(typeof this.config.maxSize != "undefined"){
32863 this.split.maxSize = this.config.maxSize;
32865 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
32866 this.hideSplitter();
32871 getHMaxSize : function(){
32872 var cmax = this.config.maxSize || 10000;
32873 var center = this.mgr.getRegion("center");
32874 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32877 getVMaxSize : function(){
32878 var cmax = this.config.maxSize || 10000;
32879 var center = this.mgr.getRegion("center");
32880 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32883 onSplitMove : function(split, newSize){
32884 this.fireEvent("resized", this, newSize);
32888 * Returns the {@link Roo.SplitBar} for this region.
32889 * @return {Roo.SplitBar}
32891 getSplitBar : function(){
32896 this.hideSplitter();
32897 Roo.bootstrap.layout.Split.superclass.hide.call(this);
32900 hideSplitter : function(){
32902 this.split.el.setLocation(-2000,-2000);
32903 this.split.el.hide();
32909 this.split.el.show();
32911 Roo.bootstrap.layout.Split.superclass.show.call(this);
32914 beforeSlide: function(){
32915 if(Roo.isGecko){// firefox overflow auto bug workaround
32916 this.bodyEl.clip();
32918 this.tabs.bodyEl.clip();
32920 if(this.activePanel){
32921 this.activePanel.getEl().clip();
32923 if(this.activePanel.beforeSlide){
32924 this.activePanel.beforeSlide();
32930 afterSlide : function(){
32931 if(Roo.isGecko){// firefox overflow auto bug workaround
32932 this.bodyEl.unclip();
32934 this.tabs.bodyEl.unclip();
32936 if(this.activePanel){
32937 this.activePanel.getEl().unclip();
32938 if(this.activePanel.afterSlide){
32939 this.activePanel.afterSlide();
32945 initAutoHide : function(){
32946 if(this.autoHide !== false){
32947 if(!this.autoHideHd){
32948 var st = new Roo.util.DelayedTask(this.slideIn, this);
32949 this.autoHideHd = {
32950 "mouseout": function(e){
32951 if(!e.within(this.el, true)){
32955 "mouseover" : function(e){
32961 this.el.on(this.autoHideHd);
32965 clearAutoHide : function(){
32966 if(this.autoHide !== false){
32967 this.el.un("mouseout", this.autoHideHd.mouseout);
32968 this.el.un("mouseover", this.autoHideHd.mouseover);
32972 clearMonitor : function(){
32973 Roo.get(document).un("click", this.slideInIf, this);
32976 // these names are backwards but not changed for compat
32977 slideOut : function(){
32978 if(this.isSlid || this.el.hasActiveFx()){
32981 this.isSlid = true;
32982 if(this.collapseBtn){
32983 this.collapseBtn.hide();
32985 this.closeBtnState = this.closeBtn.getStyle('display');
32986 this.closeBtn.hide();
32988 this.stickBtn.show();
32991 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32992 this.beforeSlide();
32993 this.el.setStyle("z-index", 10001);
32994 this.el.slideIn(this.getSlideAnchor(), {
32995 callback: function(){
32997 this.initAutoHide();
32998 Roo.get(document).on("click", this.slideInIf, this);
32999 this.fireEvent("slideshow", this);
33006 afterSlideIn : function(){
33007 this.clearAutoHide();
33008 this.isSlid = false;
33009 this.clearMonitor();
33010 this.el.setStyle("z-index", "");
33011 if(this.collapseBtn){
33012 this.collapseBtn.show();
33014 this.closeBtn.setStyle('display', this.closeBtnState);
33016 this.stickBtn.hide();
33018 this.fireEvent("slidehide", this);
33021 slideIn : function(cb){
33022 if(!this.isSlid || this.el.hasActiveFx()){
33026 this.isSlid = false;
33027 this.beforeSlide();
33028 this.el.slideOut(this.getSlideAnchor(), {
33029 callback: function(){
33030 this.el.setLeftTop(-10000, -10000);
33032 this.afterSlideIn();
33040 slideInIf : function(e){
33041 if(!e.within(this.el)){
33046 animateCollapse : function(){
33047 this.beforeSlide();
33048 this.el.setStyle("z-index", 20000);
33049 var anchor = this.getSlideAnchor();
33050 this.el.slideOut(anchor, {
33051 callback : function(){
33052 this.el.setStyle("z-index", "");
33053 this.collapsedEl.slideIn(anchor, {duration:.3});
33055 this.el.setLocation(-10000,-10000);
33057 this.fireEvent("collapsed", this);
33064 animateExpand : function(){
33065 this.beforeSlide();
33066 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33067 this.el.setStyle("z-index", 20000);
33068 this.collapsedEl.hide({
33071 this.el.slideIn(this.getSlideAnchor(), {
33072 callback : function(){
33073 this.el.setStyle("z-index", "");
33076 this.split.el.show();
33078 this.fireEvent("invalidated", this);
33079 this.fireEvent("expanded", this);
33107 getAnchor : function(){
33108 return this.anchors[this.position];
33111 getCollapseAnchor : function(){
33112 return this.canchors[this.position];
33115 getSlideAnchor : function(){
33116 return this.sanchors[this.position];
33119 getAlignAdj : function(){
33120 var cm = this.cmargins;
33121 switch(this.position){
33137 getExpandAdj : function(){
33138 var c = this.collapsedEl, cm = this.cmargins;
33139 switch(this.position){
33141 return [-(cm.right+c.getWidth()+cm.left), 0];
33144 return [cm.right+c.getWidth()+cm.left, 0];
33147 return [0, -(cm.top+cm.bottom+c.getHeight())];
33150 return [0, cm.top+cm.bottom+c.getHeight()];
33156 * Ext JS Library 1.1.1
33157 * Copyright(c) 2006-2007, Ext JS, LLC.
33159 * Originally Released Under LGPL - original licence link has changed is not relivant.
33162 * <script type="text/javascript">
33165 * These classes are private internal classes
33167 Roo.bootstrap.layout.Center = function(config){
33168 config.region = "center";
33169 Roo.bootstrap.layout.Region.call(this, config);
33170 this.visible = true;
33171 this.minWidth = config.minWidth || 20;
33172 this.minHeight = config.minHeight || 20;
33175 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33177 // center panel can't be hidden
33181 // center panel can't be hidden
33184 getMinWidth: function(){
33185 return this.minWidth;
33188 getMinHeight: function(){
33189 return this.minHeight;
33202 Roo.bootstrap.layout.North = function(config)
33204 config.region = 'north';
33205 config.cursor = 'n-resize';
33207 Roo.bootstrap.layout.Split.call(this, config);
33211 this.split.placement = Roo.bootstrap.SplitBar.TOP;
33212 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33213 this.split.el.addClass("roo-layout-split-v");
33215 var size = config.initialSize || config.height;
33216 if(typeof size != "undefined"){
33217 this.el.setHeight(size);
33220 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33222 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33226 getBox : function(){
33227 if(this.collapsed){
33228 return this.collapsedEl.getBox();
33230 var box = this.el.getBox();
33232 box.height += this.split.el.getHeight();
33237 updateBox : function(box){
33238 if(this.split && !this.collapsed){
33239 box.height -= this.split.el.getHeight();
33240 this.split.el.setLeft(box.x);
33241 this.split.el.setTop(box.y+box.height);
33242 this.split.el.setWidth(box.width);
33244 if(this.collapsed){
33245 this.updateBody(box.width, null);
33247 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33255 Roo.bootstrap.layout.South = function(config){
33256 config.region = 'south';
33257 config.cursor = 's-resize';
33258 Roo.bootstrap.layout.Split.call(this, config);
33260 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33261 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33262 this.split.el.addClass("roo-layout-split-v");
33264 var size = config.initialSize || config.height;
33265 if(typeof size != "undefined"){
33266 this.el.setHeight(size);
33270 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33271 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33272 getBox : function(){
33273 if(this.collapsed){
33274 return this.collapsedEl.getBox();
33276 var box = this.el.getBox();
33278 var sh = this.split.el.getHeight();
33285 updateBox : function(box){
33286 if(this.split && !this.collapsed){
33287 var sh = this.split.el.getHeight();
33290 this.split.el.setLeft(box.x);
33291 this.split.el.setTop(box.y-sh);
33292 this.split.el.setWidth(box.width);
33294 if(this.collapsed){
33295 this.updateBody(box.width, null);
33297 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33301 Roo.bootstrap.layout.East = function(config){
33302 config.region = "east";
33303 config.cursor = "e-resize";
33304 Roo.bootstrap.layout.Split.call(this, config);
33306 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33307 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33308 this.split.el.addClass("roo-layout-split-h");
33310 var size = config.initialSize || config.width;
33311 if(typeof size != "undefined"){
33312 this.el.setWidth(size);
33315 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33316 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33317 getBox : function(){
33318 if(this.collapsed){
33319 return this.collapsedEl.getBox();
33321 var box = this.el.getBox();
33323 var sw = this.split.el.getWidth();
33330 updateBox : function(box){
33331 if(this.split && !this.collapsed){
33332 var sw = this.split.el.getWidth();
33334 this.split.el.setLeft(box.x);
33335 this.split.el.setTop(box.y);
33336 this.split.el.setHeight(box.height);
33339 if(this.collapsed){
33340 this.updateBody(null, box.height);
33342 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33346 Roo.bootstrap.layout.West = function(config){
33347 config.region = "west";
33348 config.cursor = "w-resize";
33350 Roo.bootstrap.layout.Split.call(this, config);
33352 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33353 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33354 this.split.el.addClass("roo-layout-split-h");
33358 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33359 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33361 onRender: function(ctr, pos)
33363 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33364 var size = this.config.initialSize || this.config.width;
33365 if(typeof size != "undefined"){
33366 this.el.setWidth(size);
33370 getBox : function(){
33371 if(this.collapsed){
33372 return this.collapsedEl.getBox();
33374 var box = this.el.getBox();
33376 box.width += this.split.el.getWidth();
33381 updateBox : function(box){
33382 if(this.split && !this.collapsed){
33383 var sw = this.split.el.getWidth();
33385 this.split.el.setLeft(box.x+box.width);
33386 this.split.el.setTop(box.y);
33387 this.split.el.setHeight(box.height);
33389 if(this.collapsed){
33390 this.updateBody(null, box.height);
33392 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33395 Roo.namespace("Roo.bootstrap.panel");/*
33397 * Ext JS Library 1.1.1
33398 * Copyright(c) 2006-2007, Ext JS, LLC.
33400 * Originally Released Under LGPL - original licence link has changed is not relivant.
33403 * <script type="text/javascript">
33406 * @class Roo.ContentPanel
33407 * @extends Roo.util.Observable
33408 * A basic ContentPanel element.
33409 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
33410 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
33411 * @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
33412 * @cfg {Boolean} closable True if the panel can be closed/removed
33413 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
33414 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33415 * @cfg {Toolbar} toolbar A toolbar for this panel
33416 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
33417 * @cfg {String} title The title for this panel
33418 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33419 * @cfg {String} url Calls {@link #setUrl} with this value
33420 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33421 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
33422 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
33423 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
33426 * Create a new ContentPanel.
33427 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33428 * @param {String/Object} config A string to set only the title or a config object
33429 * @param {String} content (optional) Set the HTML content for this panel
33430 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33432 Roo.bootstrap.panel.Content = function( config){
33434 var el = config.el;
33435 var content = config.content;
33437 if(config.autoCreate){ // xtype is available if this is called from factory
33440 this.el = Roo.get(el);
33441 if(!this.el && config && config.autoCreate){
33442 if(typeof config.autoCreate == "object"){
33443 if(!config.autoCreate.id){
33444 config.autoCreate.id = config.id||el;
33446 this.el = Roo.DomHelper.append(document.body,
33447 config.autoCreate, true);
33449 var elcfg = { tag: "div",
33450 cls: "roo-layout-inactive-content",
33454 elcfg.html = config.html;
33458 this.el = Roo.DomHelper.append(document.body, elcfg , true);
33461 this.closable = false;
33462 this.loaded = false;
33463 this.active = false;
33466 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33468 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33470 this.wrapEl = this.el.wrap();
33472 if (config.toolbar.items) {
33473 ti = config.toolbar.items ;
33474 delete config.toolbar.items ;
33478 this.toolbar.render(this.wrapEl, 'before');
33479 for(var i =0;i < ti.length;i++) {
33480 // Roo.log(['add child', items[i]]);
33481 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33483 this.toolbar.items = nitems;
33484 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33485 delete config.toolbar;
33489 // xtype created footer. - not sure if will work as we normally have to render first..
33490 if (this.footer && !this.footer.el && this.footer.xtype) {
33491 if (!this.wrapEl) {
33492 this.wrapEl = this.el.wrap();
33495 this.footer.container = this.wrapEl.createChild();
33497 this.footer = Roo.factory(this.footer, Roo);
33502 if(typeof config == "string"){
33503 this.title = config;
33505 Roo.apply(this, config);
33509 this.resizeEl = Roo.get(this.resizeEl, true);
33511 this.resizeEl = this.el;
33513 // handle view.xtype
33521 * Fires when this panel is activated.
33522 * @param {Roo.ContentPanel} this
33526 * @event deactivate
33527 * Fires when this panel is activated.
33528 * @param {Roo.ContentPanel} this
33530 "deactivate" : true,
33534 * Fires when this panel is resized if fitToFrame is true.
33535 * @param {Roo.ContentPanel} this
33536 * @param {Number} width The width after any component adjustments
33537 * @param {Number} height The height after any component adjustments
33543 * Fires when this tab is created
33544 * @param {Roo.ContentPanel} this
33555 if(this.autoScroll){
33556 this.resizeEl.setStyle("overflow", "auto");
33558 // fix randome scrolling
33559 //this.el.on('scroll', function() {
33560 // Roo.log('fix random scolling');
33561 // this.scrollTo('top',0);
33564 content = content || this.content;
33566 this.setContent(content);
33568 if(config && config.url){
33569 this.setUrl(this.url, this.params, this.loadOnce);
33574 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33576 if (this.view && typeof(this.view.xtype) != 'undefined') {
33577 this.view.el = this.el.appendChild(document.createElement("div"));
33578 this.view = Roo.factory(this.view);
33579 this.view.render && this.view.render(false, '');
33583 this.fireEvent('render', this);
33586 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33588 setRegion : function(region){
33589 this.region = region;
33591 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33593 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33598 * Returns the toolbar for this Panel if one was configured.
33599 * @return {Roo.Toolbar}
33601 getToolbar : function(){
33602 return this.toolbar;
33605 setActiveState : function(active){
33606 this.active = active;
33608 this.fireEvent("deactivate", this);
33610 this.fireEvent("activate", this);
33614 * Updates this panel's element
33615 * @param {String} content The new content
33616 * @param {Boolean} loadScripts (optional) true to look for and process scripts
33618 setContent : function(content, loadScripts){
33619 this.el.update(content, loadScripts);
33622 ignoreResize : function(w, h){
33623 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33626 this.lastSize = {width: w, height: h};
33631 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33632 * @return {Roo.UpdateManager} The UpdateManager
33634 getUpdateManager : function(){
33635 return this.el.getUpdateManager();
33638 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33639 * @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:
33642 url: "your-url.php",
33643 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33644 callback: yourFunction,
33645 scope: yourObject, //(optional scope)
33648 text: "Loading...",
33653 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33654 * 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.
33655 * @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}
33656 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33657 * @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.
33658 * @return {Roo.ContentPanel} this
33661 var um = this.el.getUpdateManager();
33662 um.update.apply(um, arguments);
33668 * 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.
33669 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33670 * @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)
33671 * @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)
33672 * @return {Roo.UpdateManager} The UpdateManager
33674 setUrl : function(url, params, loadOnce){
33675 if(this.refreshDelegate){
33676 this.removeListener("activate", this.refreshDelegate);
33678 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33679 this.on("activate", this.refreshDelegate);
33680 return this.el.getUpdateManager();
33683 _handleRefresh : function(url, params, loadOnce){
33684 if(!loadOnce || !this.loaded){
33685 var updater = this.el.getUpdateManager();
33686 updater.update(url, params, this._setLoaded.createDelegate(this));
33690 _setLoaded : function(){
33691 this.loaded = true;
33695 * Returns this panel's id
33698 getId : function(){
33703 * Returns this panel's element - used by regiosn to add.
33704 * @return {Roo.Element}
33706 getEl : function(){
33707 return this.wrapEl || this.el;
33712 adjustForComponents : function(width, height)
33714 //Roo.log('adjustForComponents ');
33715 if(this.resizeEl != this.el){
33716 width -= this.el.getFrameWidth('lr');
33717 height -= this.el.getFrameWidth('tb');
33720 var te = this.toolbar.getEl();
33721 height -= te.getHeight();
33722 te.setWidth(width);
33725 var te = this.footer.getEl();
33726 Roo.log("footer:" + te.getHeight());
33728 height -= te.getHeight();
33729 te.setWidth(width);
33733 if(this.adjustments){
33734 width += this.adjustments[0];
33735 height += this.adjustments[1];
33737 return {"width": width, "height": height};
33740 setSize : function(width, height){
33741 if(this.fitToFrame && !this.ignoreResize(width, height)){
33742 if(this.fitContainer && this.resizeEl != this.el){
33743 this.el.setSize(width, height);
33745 var size = this.adjustForComponents(width, height);
33746 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33747 this.fireEvent('resize', this, size.width, size.height);
33752 * Returns this panel's title
33755 getTitle : function(){
33760 * Set this panel's title
33761 * @param {String} title
33763 setTitle : function(title){
33764 this.title = title;
33766 this.region.updatePanelTitle(this, title);
33771 * Returns true is this panel was configured to be closable
33772 * @return {Boolean}
33774 isClosable : function(){
33775 return this.closable;
33778 beforeSlide : function(){
33780 this.resizeEl.clip();
33783 afterSlide : function(){
33785 this.resizeEl.unclip();
33789 * Force a content refresh from the URL specified in the {@link #setUrl} method.
33790 * Will fail silently if the {@link #setUrl} method has not been called.
33791 * This does not activate the panel, just updates its content.
33793 refresh : function(){
33794 if(this.refreshDelegate){
33795 this.loaded = false;
33796 this.refreshDelegate();
33801 * Destroys this panel
33803 destroy : function(){
33804 this.el.removeAllListeners();
33805 var tempEl = document.createElement("span");
33806 tempEl.appendChild(this.el.dom);
33807 tempEl.innerHTML = "";
33813 * form - if the content panel contains a form - this is a reference to it.
33814 * @type {Roo.form.Form}
33818 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33819 * This contains a reference to it.
33825 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33835 * @param {Object} cfg Xtype definition of item to add.
33839 getChildContainer: function () {
33840 return this.getEl();
33845 var ret = new Roo.factory(cfg);
33850 if (cfg.xtype.match(/^Form$/)) {
33853 //if (this.footer) {
33854 // el = this.footer.container.insertSibling(false, 'before');
33856 el = this.el.createChild();
33859 this.form = new Roo.form.Form(cfg);
33862 if ( this.form.allItems.length) {
33863 this.form.render(el.dom);
33867 // should only have one of theses..
33868 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33869 // views.. should not be just added - used named prop 'view''
33871 cfg.el = this.el.appendChild(document.createElement("div"));
33874 var ret = new Roo.factory(cfg);
33876 ret.render && ret.render(false, ''); // render blank..
33886 * @class Roo.bootstrap.panel.Grid
33887 * @extends Roo.bootstrap.panel.Content
33889 * Create a new GridPanel.
33890 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
33891 * @param {Object} config A the config object
33897 Roo.bootstrap.panel.Grid = function(config)
33901 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33902 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33904 config.el = this.wrapper;
33905 //this.el = this.wrapper;
33907 if(config.toolbar){
33908 var tool_el = this.wrapper.createChild();
33909 this.toolbar = Roo.factory(config.toolbar);
33911 if (config.toolbar.items) {
33912 ti = config.toolbar.items ;
33913 delete config.toolbar.items ;
33917 this.toolbar.render(tool_el);
33918 for(var i =0;i < ti.length;i++) {
33919 // Roo.log(['add child', items[i]]);
33920 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33922 this.toolbar.items = nitems;
33924 delete config.toolbar;
33927 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
33929 config.grid.monitorWindowResize = false; // turn off autosizing
33930 config.grid.autoHeight = false;
33931 config.grid.autoWidth = false;
33933 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
33935 if (config.background) {
33936 // render grid on panel activation (if panel background)
33937 this.on('activate', function(gp) {
33938 if (!gp.grid.rendered) {
33939 gp.grid.render(el);
33940 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
33946 this.grid.render(this.wrapper);
33947 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
33950 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
33951 // ??? needed ??? config.el = this.wrapper;
33956 // xtype created footer. - not sure if will work as we normally have to render first..
33957 if (this.footer && !this.footer.el && this.footer.xtype) {
33959 var ctr = this.grid.getView().getFooterPanel(true);
33960 this.footer.dataSource = this.grid.dataSource;
33961 this.footer = Roo.factory(this.footer, Roo);
33962 this.footer.render(ctr);
33972 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
33973 getId : function(){
33974 return this.grid.id;
33978 * Returns the grid for this panel
33979 * @return {Roo.bootstrap.Table}
33981 getGrid : function(){
33985 setSize : function(width, height){
33986 if(!this.ignoreResize(width, height)){
33987 var grid = this.grid;
33988 var size = this.adjustForComponents(width, height);
33989 var gridel = grid.getGridEl();
33990 gridel.setSize(size.width, size.height);
33992 var thd = grid.getGridEl().select('thead',true).first();
33993 var tbd = grid.getGridEl().select('tbody', true).first();
33995 tbd.setSize(width, height - thd.getHeight());
34004 beforeSlide : function(){
34005 this.grid.getView().scroller.clip();
34008 afterSlide : function(){
34009 this.grid.getView().scroller.unclip();
34012 destroy : function(){
34013 this.grid.destroy();
34015 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
34020 * @class Roo.bootstrap.panel.Nest
34021 * @extends Roo.bootstrap.panel.Content
34023 * Create a new Panel, that can contain a layout.Border.
34026 * @param {Roo.BorderLayout} layout The layout for this panel
34027 * @param {String/Object} config A string to set only the title or a config object
34029 Roo.bootstrap.panel.Nest = function(config)
34031 // construct with only one argument..
34032 /* FIXME - implement nicer consturctors
34033 if (layout.layout) {
34035 layout = config.layout;
34036 delete config.layout;
34038 if (layout.xtype && !layout.getEl) {
34039 // then layout needs constructing..
34040 layout = Roo.factory(layout, Roo);
34044 config.el = config.layout.getEl();
34046 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34048 config.layout.monitorWindowResize = false; // turn off autosizing
34049 this.layout = config.layout;
34050 this.layout.getEl().addClass("roo-layout-nested-layout");
34057 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34059 setSize : function(width, height){
34060 if(!this.ignoreResize(width, height)){
34061 var size = this.adjustForComponents(width, height);
34062 var el = this.layout.getEl();
34063 el.setSize(size.width, size.height);
34064 var touch = el.dom.offsetWidth;
34065 this.layout.layout();
34066 // ie requires a double layout on the first pass
34067 if(Roo.isIE && !this.initialized){
34068 this.initialized = true;
34069 this.layout.layout();
34074 // activate all subpanels if not currently active..
34076 setActiveState : function(active){
34077 this.active = active;
34079 this.fireEvent("deactivate", this);
34083 this.fireEvent("activate", this);
34084 // not sure if this should happen before or after..
34085 if (!this.layout) {
34086 return; // should not happen..
34089 for (var r in this.layout.regions) {
34090 reg = this.layout.getRegion(r);
34091 if (reg.getActivePanel()) {
34092 //reg.showPanel(reg.getActivePanel()); // force it to activate..
34093 reg.setActivePanel(reg.getActivePanel());
34096 if (!reg.panels.length) {
34099 reg.showPanel(reg.getPanel(0));
34108 * Returns the nested BorderLayout for this panel
34109 * @return {Roo.BorderLayout}
34111 getLayout : function(){
34112 return this.layout;
34116 * Adds a xtype elements to the layout of the nested panel
34120 xtype : 'ContentPanel',
34127 xtype : 'NestedLayoutPanel',
34133 items : [ ... list of content panels or nested layout panels.. ]
34137 * @param {Object} cfg Xtype definition of item to add.
34139 addxtype : function(cfg) {
34140 return this.layout.addxtype(cfg);
34145 * Ext JS Library 1.1.1
34146 * Copyright(c) 2006-2007, Ext JS, LLC.
34148 * Originally Released Under LGPL - original licence link has changed is not relivant.
34151 * <script type="text/javascript">
34154 * @class Roo.TabPanel
34155 * @extends Roo.util.Observable
34156 * A lightweight tab container.
34160 // basic tabs 1, built from existing content
34161 var tabs = new Roo.TabPanel("tabs1");
34162 tabs.addTab("script", "View Script");
34163 tabs.addTab("markup", "View Markup");
34164 tabs.activate("script");
34166 // more advanced tabs, built from javascript
34167 var jtabs = new Roo.TabPanel("jtabs");
34168 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34170 // set up the UpdateManager
34171 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34172 var updater = tab2.getUpdateManager();
34173 updater.setDefaultUrl("ajax1.htm");
34174 tab2.on('activate', updater.refresh, updater, true);
34176 // Use setUrl for Ajax loading
34177 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34178 tab3.setUrl("ajax2.htm", null, true);
34181 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34184 jtabs.activate("jtabs-1");
34187 * Create a new TabPanel.
34188 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34189 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34191 Roo.bootstrap.panel.Tabs = function(config){
34193 * The container element for this TabPanel.
34194 * @type Roo.Element
34196 this.el = Roo.get(config.el);
34199 if(typeof config == "boolean"){
34200 this.tabPosition = config ? "bottom" : "top";
34202 Roo.apply(this, config);
34206 if(this.tabPosition == "bottom"){
34207 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34208 this.el.addClass("roo-tabs-bottom");
34210 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34211 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34212 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34214 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34216 if(this.tabPosition != "bottom"){
34217 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34218 * @type Roo.Element
34220 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34221 this.el.addClass("roo-tabs-top");
34225 this.bodyEl.setStyle("position", "relative");
34227 this.active = null;
34228 this.activateDelegate = this.activate.createDelegate(this);
34233 * Fires when the active tab changes
34234 * @param {Roo.TabPanel} this
34235 * @param {Roo.TabPanelItem} activePanel The new active tab
34239 * @event beforetabchange
34240 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34241 * @param {Roo.TabPanel} this
34242 * @param {Object} e Set cancel to true on this object to cancel the tab change
34243 * @param {Roo.TabPanelItem} tab The tab being changed to
34245 "beforetabchange" : true
34248 Roo.EventManager.onWindowResize(this.onResize, this);
34249 this.cpad = this.el.getPadding("lr");
34250 this.hiddenCount = 0;
34253 // toolbar on the tabbar support...
34254 if (this.toolbar) {
34255 alert("no toolbar support yet");
34256 this.toolbar = false;
34258 var tcfg = this.toolbar;
34259 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
34260 this.toolbar = new Roo.Toolbar(tcfg);
34261 if (Roo.isSafari) {
34262 var tbl = tcfg.container.child('table', true);
34263 tbl.setAttribute('width', '100%');
34271 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34274 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34276 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34278 tabPosition : "top",
34280 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34282 currentTabWidth : 0,
34284 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34288 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34292 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34294 preferredTabWidth : 175,
34296 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34298 resizeTabs : false,
34300 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34302 monitorResize : true,
34304 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
34309 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34310 * @param {String} id The id of the div to use <b>or create</b>
34311 * @param {String} text The text for the tab
34312 * @param {String} content (optional) Content to put in the TabPanelItem body
34313 * @param {Boolean} closable (optional) True to create a close icon on the tab
34314 * @return {Roo.TabPanelItem} The created TabPanelItem
34316 addTab : function(id, text, content, closable)
34318 var item = new Roo.bootstrap.panel.TabItem({
34322 closable : closable
34324 this.addTabItem(item);
34326 item.setContent(content);
34332 * Returns the {@link Roo.TabPanelItem} with the specified id/index
34333 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34334 * @return {Roo.TabPanelItem}
34336 getTab : function(id){
34337 return this.items[id];
34341 * Hides the {@link Roo.TabPanelItem} with the specified id/index
34342 * @param {String/Number} id The id or index of the TabPanelItem to hide.
34344 hideTab : function(id){
34345 var t = this.items[id];
34348 this.hiddenCount++;
34349 this.autoSizeTabs();
34354 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34355 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34357 unhideTab : function(id){
34358 var t = this.items[id];
34360 t.setHidden(false);
34361 this.hiddenCount--;
34362 this.autoSizeTabs();
34367 * Adds an existing {@link Roo.TabPanelItem}.
34368 * @param {Roo.TabPanelItem} item The TabPanelItem to add
34370 addTabItem : function(item){
34371 this.items[item.id] = item;
34372 this.items.push(item);
34373 // if(this.resizeTabs){
34374 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34375 // this.autoSizeTabs();
34377 // item.autoSize();
34382 * Removes a {@link Roo.TabPanelItem}.
34383 * @param {String/Number} id The id or index of the TabPanelItem to remove.
34385 removeTab : function(id){
34386 var items = this.items;
34387 var tab = items[id];
34388 if(!tab) { return; }
34389 var index = items.indexOf(tab);
34390 if(this.active == tab && items.length > 1){
34391 var newTab = this.getNextAvailable(index);
34396 this.stripEl.dom.removeChild(tab.pnode.dom);
34397 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34398 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34400 items.splice(index, 1);
34401 delete this.items[tab.id];
34402 tab.fireEvent("close", tab);
34403 tab.purgeListeners();
34404 this.autoSizeTabs();
34407 getNextAvailable : function(start){
34408 var items = this.items;
34410 // look for a next tab that will slide over to
34411 // replace the one being removed
34412 while(index < items.length){
34413 var item = items[++index];
34414 if(item && !item.isHidden()){
34418 // if one isn't found select the previous tab (on the left)
34421 var item = items[--index];
34422 if(item && !item.isHidden()){
34430 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34431 * @param {String/Number} id The id or index of the TabPanelItem to disable.
34433 disableTab : function(id){
34434 var tab = this.items[id];
34435 if(tab && this.active != tab){
34441 * Enables a {@link Roo.TabPanelItem} that is disabled.
34442 * @param {String/Number} id The id or index of the TabPanelItem to enable.
34444 enableTab : function(id){
34445 var tab = this.items[id];
34450 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34451 * @param {String/Number} id The id or index of the TabPanelItem to activate.
34452 * @return {Roo.TabPanelItem} The TabPanelItem.
34454 activate : function(id){
34455 var tab = this.items[id];
34459 if(tab == this.active || tab.disabled){
34463 this.fireEvent("beforetabchange", this, e, tab);
34464 if(e.cancel !== true && !tab.disabled){
34466 this.active.hide();
34468 this.active = this.items[id];
34469 this.active.show();
34470 this.fireEvent("tabchange", this, this.active);
34476 * Gets the active {@link Roo.TabPanelItem}.
34477 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34479 getActiveTab : function(){
34480 return this.active;
34484 * Updates the tab body element to fit the height of the container element
34485 * for overflow scrolling
34486 * @param {Number} targetHeight (optional) Override the starting height from the elements height
34488 syncHeight : function(targetHeight){
34489 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34490 var bm = this.bodyEl.getMargins();
34491 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34492 this.bodyEl.setHeight(newHeight);
34496 onResize : function(){
34497 if(this.monitorResize){
34498 this.autoSizeTabs();
34503 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34505 beginUpdate : function(){
34506 this.updating = true;
34510 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34512 endUpdate : function(){
34513 this.updating = false;
34514 this.autoSizeTabs();
34518 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34520 autoSizeTabs : function(){
34521 var count = this.items.length;
34522 var vcount = count - this.hiddenCount;
34523 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34526 var w = Math.max(this.el.getWidth() - this.cpad, 10);
34527 var availWidth = Math.floor(w / vcount);
34528 var b = this.stripBody;
34529 if(b.getWidth() > w){
34530 var tabs = this.items;
34531 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34532 if(availWidth < this.minTabWidth){
34533 /*if(!this.sleft){ // incomplete scrolling code
34534 this.createScrollButtons();
34537 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34540 if(this.currentTabWidth < this.preferredTabWidth){
34541 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34547 * Returns the number of tabs in this TabPanel.
34550 getCount : function(){
34551 return this.items.length;
34555 * Resizes all the tabs to the passed width
34556 * @param {Number} The new width
34558 setTabWidth : function(width){
34559 this.currentTabWidth = width;
34560 for(var i = 0, len = this.items.length; i < len; i++) {
34561 if(!this.items[i].isHidden()) {
34562 this.items[i].setWidth(width);
34568 * Destroys this TabPanel
34569 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34571 destroy : function(removeEl){
34572 Roo.EventManager.removeResizeListener(this.onResize, this);
34573 for(var i = 0, len = this.items.length; i < len; i++){
34574 this.items[i].purgeListeners();
34576 if(removeEl === true){
34577 this.el.update("");
34582 createStrip : function(container)
34584 var strip = document.createElement("nav");
34585 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34586 container.appendChild(strip);
34590 createStripList : function(strip)
34592 // div wrapper for retard IE
34593 // returns the "tr" element.
34594 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34595 //'<div class="x-tabs-strip-wrap">'+
34596 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34597 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34598 return strip.firstChild; //.firstChild.firstChild.firstChild;
34600 createBody : function(container)
34602 var body = document.createElement("div");
34603 Roo.id(body, "tab-body");
34604 //Roo.fly(body).addClass("x-tabs-body");
34605 Roo.fly(body).addClass("tab-content");
34606 container.appendChild(body);
34609 createItemBody :function(bodyEl, id){
34610 var body = Roo.getDom(id);
34612 body = document.createElement("div");
34615 //Roo.fly(body).addClass("x-tabs-item-body");
34616 Roo.fly(body).addClass("tab-pane");
34617 bodyEl.insertBefore(body, bodyEl.firstChild);
34621 createStripElements : function(stripEl, text, closable)
34623 var td = document.createElement("li"); // was td..
34624 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34625 //stripEl.appendChild(td);
34627 td.className = "x-tabs-closable";
34628 if(!this.closeTpl){
34629 this.closeTpl = new Roo.Template(
34630 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34631 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34632 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
34635 var el = this.closeTpl.overwrite(td, {"text": text});
34636 var close = el.getElementsByTagName("div")[0];
34637 var inner = el.getElementsByTagName("em")[0];
34638 return {"el": el, "close": close, "inner": inner};
34641 // not sure what this is..
34643 //this.tabTpl = new Roo.Template(
34644 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34645 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34647 this.tabTpl = new Roo.Template(
34649 '<span unselectable="on"' +
34650 (this.disableTooltips ? '' : ' title="{text}"') +
34651 ' >{text}</span></span></a>'
34655 var el = this.tabTpl.overwrite(td, {"text": text});
34656 var inner = el.getElementsByTagName("span")[0];
34657 return {"el": el, "inner": inner};
34665 * @class Roo.TabPanelItem
34666 * @extends Roo.util.Observable
34667 * Represents an individual item (tab plus body) in a TabPanel.
34668 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34669 * @param {String} id The id of this TabPanelItem
34670 * @param {String} text The text for the tab of this TabPanelItem
34671 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34673 Roo.bootstrap.panel.TabItem = function(config){
34675 * The {@link Roo.TabPanel} this TabPanelItem belongs to
34676 * @type Roo.TabPanel
34678 this.tabPanel = config.panel;
34680 * The id for this TabPanelItem
34683 this.id = config.id;
34685 this.disabled = false;
34687 this.text = config.text;
34689 this.loaded = false;
34690 this.closable = config.closable;
34693 * The body element for this TabPanelItem.
34694 * @type Roo.Element
34696 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34697 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34698 this.bodyEl.setStyle("display", "block");
34699 this.bodyEl.setStyle("zoom", "1");
34700 //this.hideAction();
34702 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34704 this.el = Roo.get(els.el);
34705 this.inner = Roo.get(els.inner, true);
34706 this.textEl = Roo.get(this.el.dom.firstChild, true);
34707 this.pnode = Roo.get(els.el.parentNode, true);
34708 this.el.on("mousedown", this.onTabMouseDown, this);
34709 this.el.on("click", this.onTabClick, this);
34711 if(config.closable){
34712 var c = Roo.get(els.close, true);
34713 c.dom.title = this.closeText;
34714 c.addClassOnOver("close-over");
34715 c.on("click", this.closeClick, this);
34721 * Fires when this tab becomes the active tab.
34722 * @param {Roo.TabPanel} tabPanel The parent TabPanel
34723 * @param {Roo.TabPanelItem} this
34727 * @event beforeclose
34728 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34729 * @param {Roo.TabPanelItem} this
34730 * @param {Object} e Set cancel to true on this object to cancel the close.
34732 "beforeclose": true,
34735 * Fires when this tab is closed.
34736 * @param {Roo.TabPanelItem} this
34740 * @event deactivate
34741 * Fires when this tab is no longer the active tab.
34742 * @param {Roo.TabPanel} tabPanel The parent TabPanel
34743 * @param {Roo.TabPanelItem} this
34745 "deactivate" : true
34747 this.hidden = false;
34749 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34752 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34754 purgeListeners : function(){
34755 Roo.util.Observable.prototype.purgeListeners.call(this);
34756 this.el.removeAllListeners();
34759 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34762 this.pnode.addClass("active");
34765 this.tabPanel.stripWrap.repaint();
34767 this.fireEvent("activate", this.tabPanel, this);
34771 * Returns true if this tab is the active tab.
34772 * @return {Boolean}
34774 isActive : function(){
34775 return this.tabPanel.getActiveTab() == this;
34779 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34782 this.pnode.removeClass("active");
34784 this.fireEvent("deactivate", this.tabPanel, this);
34787 hideAction : function(){
34788 this.bodyEl.hide();
34789 this.bodyEl.setStyle("position", "absolute");
34790 this.bodyEl.setLeft("-20000px");
34791 this.bodyEl.setTop("-20000px");
34794 showAction : function(){
34795 this.bodyEl.setStyle("position", "relative");
34796 this.bodyEl.setTop("");
34797 this.bodyEl.setLeft("");
34798 this.bodyEl.show();
34802 * Set the tooltip for the tab.
34803 * @param {String} tooltip The tab's tooltip
34805 setTooltip : function(text){
34806 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34807 this.textEl.dom.qtip = text;
34808 this.textEl.dom.removeAttribute('title');
34810 this.textEl.dom.title = text;
34814 onTabClick : function(e){
34815 e.preventDefault();
34816 this.tabPanel.activate(this.id);
34819 onTabMouseDown : function(e){
34820 e.preventDefault();
34821 this.tabPanel.activate(this.id);
34824 getWidth : function(){
34825 return this.inner.getWidth();
34828 setWidth : function(width){
34829 var iwidth = width - this.pnode.getPadding("lr");
34830 this.inner.setWidth(iwidth);
34831 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34832 this.pnode.setWidth(width);
34836 * Show or hide the tab
34837 * @param {Boolean} hidden True to hide or false to show.
34839 setHidden : function(hidden){
34840 this.hidden = hidden;
34841 this.pnode.setStyle("display", hidden ? "none" : "");
34845 * Returns true if this tab is "hidden"
34846 * @return {Boolean}
34848 isHidden : function(){
34849 return this.hidden;
34853 * Returns the text for this tab
34856 getText : function(){
34860 autoSize : function(){
34861 //this.el.beginMeasure();
34862 this.textEl.setWidth(1);
34864 * #2804 [new] Tabs in Roojs
34865 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34867 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34868 //this.el.endMeasure();
34872 * Sets the text for the tab (Note: this also sets the tooltip text)
34873 * @param {String} text The tab's text and tooltip
34875 setText : function(text){
34877 this.textEl.update(text);
34878 this.setTooltip(text);
34879 //if(!this.tabPanel.resizeTabs){
34880 // this.autoSize();
34884 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
34886 activate : function(){
34887 this.tabPanel.activate(this.id);
34891 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
34893 disable : function(){
34894 if(this.tabPanel.active != this){
34895 this.disabled = true;
34896 this.pnode.addClass("disabled");
34901 * Enables this TabPanelItem if it was previously disabled.
34903 enable : function(){
34904 this.disabled = false;
34905 this.pnode.removeClass("disabled");
34909 * Sets the content for this TabPanelItem.
34910 * @param {String} content The content
34911 * @param {Boolean} loadScripts true to look for and load scripts
34913 setContent : function(content, loadScripts){
34914 this.bodyEl.update(content, loadScripts);
34918 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
34919 * @return {Roo.UpdateManager} The UpdateManager
34921 getUpdateManager : function(){
34922 return this.bodyEl.getUpdateManager();
34926 * Set a URL to be used to load the content for this TabPanelItem.
34927 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
34928 * @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)
34929 * @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)
34930 * @return {Roo.UpdateManager} The UpdateManager
34932 setUrl : function(url, params, loadOnce){
34933 if(this.refreshDelegate){
34934 this.un('activate', this.refreshDelegate);
34936 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34937 this.on("activate", this.refreshDelegate);
34938 return this.bodyEl.getUpdateManager();
34942 _handleRefresh : function(url, params, loadOnce){
34943 if(!loadOnce || !this.loaded){
34944 var updater = this.bodyEl.getUpdateManager();
34945 updater.update(url, params, this._setLoaded.createDelegate(this));
34950 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
34951 * Will fail silently if the setUrl method has not been called.
34952 * This does not activate the panel, just updates its content.
34954 refresh : function(){
34955 if(this.refreshDelegate){
34956 this.loaded = false;
34957 this.refreshDelegate();
34962 _setLoaded : function(){
34963 this.loaded = true;
34967 closeClick : function(e){
34970 this.fireEvent("beforeclose", this, o);
34971 if(o.cancel !== true){
34972 this.tabPanel.removeTab(this.id);
34976 * The text displayed in the tooltip for the close icon.
34979 closeText : "Close this tab"