4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
107 // fill in the extra attributes
108 if (this.xattr && typeof(this.xattr) =='object') {
109 for (var i in this.xattr) {
110 cfg[i] = this.xattr[i];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
193 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195 return this.addxtypeChild(tree,cntr, is_body);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
234 addxtypeChild : function (tree, cntr, is_body)
236 Roo.debug && Roo.log('addxtypeChild:' + cntr);
238 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
241 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242 (typeof(tree['flexy:foreach']) != 'undefined');
246 skip_children = false;
247 // render the element if it's not BODY.
250 cn = Roo.factory(tree);
252 cn.parentType = this.xtype; //??
253 cn.parentId = this.id;
255 var build_from_html = Roo.XComponent.build_from_html;
258 // does the container contain child eleemnts with 'xtype' attributes.
259 // that match this xtype..
260 // note - when we render we create these as well..
261 // so we should check to see if body has xtype set.
262 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
264 var self_cntr_el = Roo.get(this[cntr](false));
265 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
267 //Roo.log(Roo.XComponent.build_from_html);
268 //Roo.log("got echild:");
271 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272 // and are not displayed -this causes this to use up the wrong element when matching.
273 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
276 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
283 //echild.dom.removeAttribute('xtype');
285 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286 Roo.debug && Roo.log(self_cntr_el);
287 Roo.debug && Roo.log(echild);
288 Roo.debug && Roo.log(cn);
294 // if object has flexy:if - then it may or may not be rendered.
295 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
296 // skip a flexy if element.
297 Roo.debug && Roo.log('skipping render');
298 Roo.debug && Roo.log(tree);
300 Roo.debug && Roo.log('skipping all children');
301 skip_children = true;
306 // actually if flexy:foreach is found, we really want to create
307 // multiple copies here...
309 //Roo.log(this[cntr]());
310 // some elements do not have render methods.. like the layouts...
311 cn.render && cn.render(this[cntr](true));
313 // then add the element..
321 if (typeof (tree.menu) != 'undefined') {
322 tree.menu.parentType = cn.xtype;
323 tree.menu.triggerEl = cn.el;
324 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
328 if (!tree.items || !tree.items.length) {
330 //Roo.log(["no children", this]);
335 var items = tree.items;
338 //Roo.log(items.length);
340 if (!skip_children) {
341 for(var i =0;i < items.length;i++) {
342 // Roo.log(['add child', items[i]]);
343 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
349 //Roo.log("fire childrenrendered");
351 cn.fireEvent('childrenrendered', this);
356 * Show a component - removes 'hidden' class
361 this.el.removeClass('hidden');
365 * Hide a component - adds 'hidden' class
369 if (this.el && !this.el.hasClass('hidden')) {
370 this.el.addClass('hidden');
384 * @class Roo.bootstrap.Body
385 * @extends Roo.bootstrap.Component
386 * Bootstrap Body class
390 * @param {Object} config The config object
393 Roo.bootstrap.Body = function(config){
394 Roo.bootstrap.Body.superclass.constructor.call(this, config);
395 this.el = Roo.get(document.body);
396 if (this.cls && this.cls.length) {
397 Roo.get(document.body).addClass(this.cls);
401 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
403 is_body : true,// just to make sure it's constructed?
408 onRender : function(ct, position)
410 /* Roo.log("Roo.bootstrap.Body - onRender");
411 if (this.cls && this.cls.length) {
412 Roo.get(document.body).addClass(this.cls);
432 * @class Roo.bootstrap.ButtonGroup
433 * @extends Roo.bootstrap.Component
434 * Bootstrap ButtonGroup class
435 * @cfg {String} size lg | sm | xs (default empty normal)
436 * @cfg {String} align vertical | justified (default none)
437 * @cfg {String} direction up | down (default down)
438 * @cfg {Boolean} toolbar false | true
439 * @cfg {Boolean} btn true | false
444 * @param {Object} config The config object
447 Roo.bootstrap.ButtonGroup = function(config){
448 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
459 getAutoCreate : function(){
465 cfg.html = this.html || cfg.html;
476 if (['vertical','justified'].indexOf(this.align)!==-1) {
477 cfg.cls = 'btn-group-' + this.align;
479 if (this.align == 'justified') {
480 console.log(this.items);
484 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
485 cfg.cls += ' btn-group-' + this.size;
488 if (this.direction == 'up') {
489 cfg.cls += ' dropup' ;
505 * @class Roo.bootstrap.Button
506 * @extends Roo.bootstrap.Component
507 * Bootstrap Button class
508 * @cfg {String} html The button content
509 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
510 * @cfg {String} size ( lg | sm | xs)
511 * @cfg {String} tag ( a | input | submit)
512 * @cfg {String} href empty or href
513 * @cfg {Boolean} disabled default false;
514 * @cfg {Boolean} isClose default false;
515 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
516 * @cfg {String} badge text for badge
517 * @cfg {String} theme default
518 * @cfg {Boolean} inverse
519 * @cfg {Boolean} toggle
520 * @cfg {String} ontext text for on toggle state
521 * @cfg {String} offtext text for off toggle state
522 * @cfg {Boolean} defaulton
523 * @cfg {Boolean} preventDefault default true
524 * @cfg {Boolean} removeClass remove the standard class..
525 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
528 * Create a new button
529 * @param {Object} config The config object
533 Roo.bootstrap.Button = function(config){
534 Roo.bootstrap.Button.superclass.constructor.call(this, config);
539 * When a butotn is pressed
540 * @param {Roo.bootstrap.Button} this
541 * @param {Roo.EventObject} e
546 * After the button has been toggles
547 * @param {Roo.EventObject} e
548 * @param {boolean} pressed (also available as button.pressed)
554 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
572 preventDefault: true,
581 getAutoCreate : function(){
589 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
590 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
595 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
597 if (this.toggle == true) {
600 cls: 'slider-frame roo-button',
605 'data-off-text':'OFF',
606 cls: 'slider-button',
612 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
613 cfg.cls += ' '+this.weight;
622 cfg["aria-hidden"] = true;
624 cfg.html = "×";
630 if (this.theme==='default') {
631 cfg.cls = 'btn roo-button';
633 //if (this.parentType != 'Navbar') {
634 this.weight = this.weight.length ? this.weight : 'default';
636 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
638 cfg.cls += ' btn-' + this.weight;
640 } else if (this.theme==='glow') {
643 cfg.cls = 'btn-glow roo-button';
645 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
647 cfg.cls += ' ' + this.weight;
653 this.cls += ' inverse';
658 cfg.cls += ' active';
662 cfg.disabled = 'disabled';
666 Roo.log('changing to ul' );
668 this.glyphicon = 'caret';
671 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
673 //gsRoo.log(this.parentType);
674 if (this.parentType === 'Navbar' && !this.parent().bar) {
675 Roo.log('changing to li?');
684 href : this.href || '#'
687 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
688 cfg.cls += ' dropdown';
695 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
697 if (this.glyphicon) {
698 cfg.html = ' ' + cfg.html;
703 cls: 'glyphicon glyphicon-' + this.glyphicon
713 // cfg.cls='btn roo-button';
717 var value = cfg.html;
722 cls: 'glyphicon glyphicon-' + this.glyphicon,
741 cfg.cls += ' dropdown';
742 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745 if (cfg.tag !== 'a' && this.href !== '') {
746 throw "Tag must be a to set href.";
747 } else if (this.href.length > 0) {
748 cfg.href = this.href;
751 if(this.removeClass){
756 cfg.target = this.target;
761 initEvents: function() {
762 // Roo.log('init events?');
763 // Roo.log(this.el.dom);
766 if (typeof (this.menu) != 'undefined') {
767 this.menu.parentType = this.xtype;
768 this.menu.triggerEl = this.el;
769 this.addxtype(Roo.apply({}, this.menu));
773 if (this.el.hasClass('roo-button')) {
774 this.el.on('click', this.onClick, this);
776 this.el.select('.roo-button').on('click', this.onClick, this);
779 if(this.removeClass){
780 this.el.on('click', this.onClick, this);
783 this.el.enableDisplayMode();
786 onClick : function(e)
793 Roo.log('button on click ');
794 if(this.preventDefault){
797 if (this.pressed === true || this.pressed === false) {
798 this.pressed = !this.pressed;
799 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
800 this.fireEvent('toggle', this, e, this.pressed);
804 this.fireEvent('click', this, e);
808 * Enables this button
812 this.disabled = false;
813 this.el.removeClass('disabled');
817 * Disable this button
821 this.disabled = true;
822 this.el.addClass('disabled');
825 * sets the active state on/off,
826 * @param {Boolean} state (optional) Force a particular state
828 setActive : function(v) {
830 this.el[v ? 'addClass' : 'removeClass']('active');
833 * toggles the current active state
835 toggleActive : function()
837 var active = this.el.hasClass('active');
838 this.setActive(!active);
842 setText : function(str)
844 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
848 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
871 * @class Roo.bootstrap.Column
872 * @extends Roo.bootstrap.Component
873 * Bootstrap Column class
874 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
875 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
876 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
877 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
878 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
879 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
880 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
881 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884 * @cfg {Boolean} hidden (true|false) hide the element
885 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
886 * @cfg {String} fa (ban|check|...) font awesome icon
887 * @cfg {Number} fasize (1|2|....) font awsome size
889 * @cfg {String} icon (info-sign|check|...) glyphicon name
891 * @cfg {String} html content of column.
894 * Create a new Column
895 * @param {Object} config The config object
898 Roo.bootstrap.Column = function(config){
899 Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
920 getAutoCreate : function(){
921 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
929 ['xs','sm','md','lg'].map(function(size){
930 //Roo.log( size + ':' + settings[size]);
932 if (settings[size+'off'] !== false) {
933 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936 if (settings[size] === false) {
940 if (!settings[size]) { // 0 = hidden
941 cfg.cls += ' hidden-' + size;
944 cfg.cls += ' col-' + size + '-' + settings[size];
949 cfg.cls += ' hidden';
952 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
953 cfg.cls +=' alert alert-' + this.alert;
957 if (this.html.length) {
958 cfg.html = this.html;
962 if (this.fasize > 1) {
963 fasize = ' fa-' + this.fasize + 'x';
965 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
970 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
989 * @class Roo.bootstrap.Container
990 * @extends Roo.bootstrap.Component
991 * Bootstrap Container class
992 * @cfg {Boolean} jumbotron is it a jumbotron element
993 * @cfg {String} html content of element
994 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
995 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
996 * @cfg {String} header content of header (for panel)
997 * @cfg {String} footer content of footer (for panel)
998 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
999 * @cfg {String} tag (header|aside|section) type of HTML tag.
1000 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1001 * @cfg {String} fa font awesome icon
1002 * @cfg {String} icon (info-sign|check|...) glyphicon name
1003 * @cfg {Boolean} hidden (true|false) hide the element
1004 * @cfg {Boolean} expandable (true|false) default false
1005 * @cfg {Boolean} expanded (true|false) default true
1006 * @cfg {String} rheader contet on the right of header
1007 * @cfg {Boolean} clickable (true|false) default false
1011 * Create a new Container
1012 * @param {Object} config The config object
1015 Roo.bootstrap.Container = function(config){
1016 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1022 * After the panel has been expand
1024 * @param {Roo.bootstrap.Container} this
1029 * After the panel has been collapsed
1031 * @param {Roo.bootstrap.Container} this
1036 * When a element is chick
1037 * @param {Roo.bootstrap.Container} this
1038 * @param {Roo.EventObject} e
1044 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1062 getChildContainer : function() {
1068 if (this.panel.length) {
1069 return this.el.select('.panel-body',true).first();
1076 getAutoCreate : function(){
1079 tag : this.tag || 'div',
1083 if (this.jumbotron) {
1084 cfg.cls = 'jumbotron';
1089 // - this is applied by the parent..
1091 // cfg.cls = this.cls + '';
1094 if (this.sticky.length) {
1096 var bd = Roo.get(document.body);
1097 if (!bd.hasClass('bootstrap-sticky')) {
1098 bd.addClass('bootstrap-sticky');
1099 Roo.select('html',true).setStyle('height', '100%');
1102 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1106 if (this.well.length) {
1107 switch (this.well) {
1110 cfg.cls +=' well well-' +this.well;
1119 cfg.cls += ' hidden';
1123 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1124 cfg.cls +=' alert alert-' + this.alert;
1129 if (this.panel.length) {
1130 cfg.cls += ' panel panel-' + this.panel;
1132 if (this.header.length) {
1136 if(this.expandable){
1138 cfg.cls = cfg.cls + ' expandable';
1142 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1150 cls : 'panel-title',
1151 html : (this.expandable ? ' ' : '') + this.header
1155 cls: 'panel-header-right',
1161 cls : 'panel-heading',
1162 style : this.expandable ? 'cursor: pointer' : '',
1170 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1175 if (this.footer.length) {
1177 cls : 'panel-footer',
1186 body.html = this.html || cfg.html;
1187 // prefix with the icons..
1189 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1197 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1198 cfg.cls = 'container';
1204 initEvents: function()
1206 if(this.expandable){
1207 var headerEl = this.headerEl();
1210 headerEl.on('click', this.onToggleClick, this);
1215 this.el.on('click', this.onClick, this);
1220 onToggleClick : function()
1222 var headerEl = this.headerEl();
1238 if(this.fireEvent('expand', this)) {
1240 this.expanded = true;
1242 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1244 this.el.select('.panel-body',true).first().removeClass('hide');
1246 var toggleEl = this.toggleEl();
1252 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1257 collapse : function()
1259 if(this.fireEvent('collapse', this)) {
1261 this.expanded = false;
1263 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1264 this.el.select('.panel-body',true).first().addClass('hide');
1266 var toggleEl = this.toggleEl();
1272 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1276 toggleEl : function()
1278 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1282 return this.el.select('.panel-heading .fa',true).first();
1285 headerEl : function()
1287 if(!this.el || !this.panel.length || !this.header.length){
1291 return this.el.select('.panel-heading',true).first()
1294 titleEl : function()
1296 if(!this.el || !this.panel.length || !this.header.length){
1300 return this.el.select('.panel-title',true).first();
1303 setTitle : function(v)
1305 var titleEl = this.titleEl();
1311 titleEl.dom.innerHTML = v;
1314 getTitle : function()
1317 var titleEl = this.titleEl();
1323 return titleEl.dom.innerHTML;
1326 setRightTitle : function(v)
1328 var t = this.el.select('.panel-header-right',true).first();
1334 t.dom.innerHTML = v;
1337 onClick : function(e)
1341 this.fireEvent('click', this, e);
1355 * @class Roo.bootstrap.Img
1356 * @extends Roo.bootstrap.Component
1357 * Bootstrap Img class
1358 * @cfg {Boolean} imgResponsive false | true
1359 * @cfg {String} border rounded | circle | thumbnail
1360 * @cfg {String} src image source
1361 * @cfg {String} alt image alternative text
1362 * @cfg {String} href a tag href
1363 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1364 * @cfg {String} xsUrl xs image source
1365 * @cfg {String} smUrl sm image source
1366 * @cfg {String} mdUrl md image source
1367 * @cfg {String} lgUrl lg image source
1370 * Create a new Input
1371 * @param {Object} config The config object
1374 Roo.bootstrap.Img = function(config){
1375 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1381 * The img click event for the img.
1382 * @param {Roo.EventObject} e
1388 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1390 imgResponsive: true,
1400 getAutoCreate : function()
1402 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1403 return this.createSingleImg();
1408 cls: 'roo-image-responsive-group',
1413 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1415 if(!_this[size + 'Url']){
1421 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1422 html: _this.html || cfg.html,
1423 src: _this[size + 'Url']
1426 img.cls += ' roo-image-responsive-' + size;
1428 var s = ['xs', 'sm', 'md', 'lg'];
1430 s.splice(s.indexOf(size), 1);
1432 Roo.each(s, function(ss){
1433 img.cls += ' hidden-' + ss;
1436 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1437 cfg.cls += ' img-' + _this.border;
1441 cfg.alt = _this.alt;
1454 a.target = _this.target;
1458 cfg.cn.push((_this.href) ? a : img);
1465 createSingleImg : function()
1469 cls: (this.imgResponsive) ? 'img-responsive' : '',
1471 src : 'about:blank' // just incase src get's set to undefined?!?
1474 cfg.html = this.html || cfg.html;
1476 cfg.src = this.src || cfg.src;
1478 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1479 cfg.cls += ' img-' + this.border;
1496 a.target = this.target;
1501 return (this.href) ? a : cfg;
1504 initEvents: function()
1507 this.el.on('click', this.onClick, this);
1512 onClick : function(e)
1514 Roo.log('img onclick');
1515 this.fireEvent('click', this, e);
1529 * @class Roo.bootstrap.Link
1530 * @extends Roo.bootstrap.Component
1531 * Bootstrap Link Class
1532 * @cfg {String} alt image alternative text
1533 * @cfg {String} href a tag href
1534 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1535 * @cfg {String} html the content of the link.
1536 * @cfg {String} anchor name for the anchor link
1537 * @cfg {String} fa - favicon
1539 * @cfg {Boolean} preventDefault (true | false) default false
1543 * Create a new Input
1544 * @param {Object} config The config object
1547 Roo.bootstrap.Link = function(config){
1548 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1554 * The img click event for the img.
1555 * @param {Roo.EventObject} e
1561 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1565 preventDefault: false,
1571 getAutoCreate : function()
1573 var html = this.html || '';
1575 if (this.fa !== false) {
1576 html = '<i class="fa fa-' + this.fa + '"></i>';
1581 // anchor's do not require html/href...
1582 if (this.anchor === false) {
1584 cfg.href = this.href || '#';
1586 cfg.name = this.anchor;
1587 if (this.html !== false || this.fa !== false) {
1590 if (this.href !== false) {
1591 cfg.href = this.href;
1595 if(this.alt !== false){
1600 if(this.target !== false) {
1601 cfg.target = this.target;
1607 initEvents: function() {
1609 if(!this.href || this.preventDefault){
1610 this.el.on('click', this.onClick, this);
1614 onClick : function(e)
1616 if(this.preventDefault){
1619 //Roo.log('img onclick');
1620 this.fireEvent('click', this, e);
1633 * @class Roo.bootstrap.Header
1634 * @extends Roo.bootstrap.Component
1635 * Bootstrap Header class
1636 * @cfg {String} html content of header
1637 * @cfg {Number} level (1|2|3|4|5|6) default 1
1640 * Create a new Header
1641 * @param {Object} config The config object
1645 Roo.bootstrap.Header = function(config){
1646 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1649 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1657 getAutoCreate : function(){
1662 tag: 'h' + (1 *this.level),
1663 html: this.html || ''
1675 * Ext JS Library 1.1.1
1676 * Copyright(c) 2006-2007, Ext JS, LLC.
1678 * Originally Released Under LGPL - original licence link has changed is not relivant.
1681 * <script type="text/javascript">
1685 * @class Roo.bootstrap.MenuMgr
1686 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1689 Roo.bootstrap.MenuMgr = function(){
1690 var menus, active, groups = {}, attached = false, lastShow = new Date();
1692 // private - called when first menu is created
1695 active = new Roo.util.MixedCollection();
1696 Roo.get(document).addKeyListener(27, function(){
1697 if(active.length > 0){
1705 if(active && active.length > 0){
1706 var c = active.clone();
1716 if(active.length < 1){
1717 Roo.get(document).un("mouseup", onMouseDown);
1725 var last = active.last();
1726 lastShow = new Date();
1729 Roo.get(document).on("mouseup", onMouseDown);
1734 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1735 m.parentMenu.activeChild = m;
1736 }else if(last && last.isVisible()){
1737 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1742 function onBeforeHide(m){
1744 m.activeChild.hide();
1746 if(m.autoHideTimer){
1747 clearTimeout(m.autoHideTimer);
1748 delete m.autoHideTimer;
1753 function onBeforeShow(m){
1754 var pm = m.parentMenu;
1755 if(!pm && !m.allowOtherMenus){
1757 }else if(pm && pm.activeChild && active != m){
1758 pm.activeChild.hide();
1762 // private this should really trigger on mouseup..
1763 function onMouseDown(e){
1764 Roo.log("on Mouse Up");
1766 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1767 Roo.log("MenuManager hideAll");
1776 function onBeforeCheck(mi, state){
1778 var g = groups[mi.group];
1779 for(var i = 0, l = g.length; i < l; i++){
1781 g[i].setChecked(false);
1790 * Hides all menus that are currently visible
1792 hideAll : function(){
1797 register : function(menu){
1801 menus[menu.id] = menu;
1802 menu.on("beforehide", onBeforeHide);
1803 menu.on("hide", onHide);
1804 menu.on("beforeshow", onBeforeShow);
1805 menu.on("show", onShow);
1807 if(g && menu.events["checkchange"]){
1811 groups[g].push(menu);
1812 menu.on("checkchange", onCheck);
1817 * Returns a {@link Roo.menu.Menu} object
1818 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1819 * be used to generate and return a new Menu instance.
1821 get : function(menu){
1822 if(typeof menu == "string"){ // menu id
1824 }else if(menu.events){ // menu instance
1827 /*else if(typeof menu.length == 'number'){ // array of menu items?
1828 return new Roo.bootstrap.Menu({items:menu});
1829 }else{ // otherwise, must be a config
1830 return new Roo.bootstrap.Menu(menu);
1837 unregister : function(menu){
1838 delete menus[menu.id];
1839 menu.un("beforehide", onBeforeHide);
1840 menu.un("hide", onHide);
1841 menu.un("beforeshow", onBeforeShow);
1842 menu.un("show", onShow);
1844 if(g && menu.events["checkchange"]){
1845 groups[g].remove(menu);
1846 menu.un("checkchange", onCheck);
1851 registerCheckable : function(menuItem){
1852 var g = menuItem.group;
1857 groups[g].push(menuItem);
1858 menuItem.on("beforecheckchange", onBeforeCheck);
1863 unregisterCheckable : function(menuItem){
1864 var g = menuItem.group;
1866 groups[g].remove(menuItem);
1867 menuItem.un("beforecheckchange", onBeforeCheck);
1879 * @class Roo.bootstrap.Menu
1880 * @extends Roo.bootstrap.Component
1881 * Bootstrap Menu class - container for MenuItems
1882 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1883 * @cfg {bool} hidden if the menu should be hidden when rendered.
1884 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1885 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1889 * @param {Object} config The config object
1893 Roo.bootstrap.Menu = function(config){
1894 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1895 if (this.registerMenu && this.type != 'treeview') {
1896 Roo.bootstrap.MenuMgr.register(this);
1901 * Fires before this menu is displayed
1902 * @param {Roo.menu.Menu} this
1907 * Fires before this menu is hidden
1908 * @param {Roo.menu.Menu} this
1913 * Fires after this menu is displayed
1914 * @param {Roo.menu.Menu} this
1919 * Fires after this menu is hidden
1920 * @param {Roo.menu.Menu} this
1925 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1926 * @param {Roo.menu.Menu} this
1927 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1928 * @param {Roo.EventObject} e
1933 * Fires when the mouse is hovering over this menu
1934 * @param {Roo.menu.Menu} this
1935 * @param {Roo.EventObject} e
1936 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1941 * Fires when the mouse exits this menu
1942 * @param {Roo.menu.Menu} this
1943 * @param {Roo.EventObject} e
1944 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1949 * Fires when a menu item contained in this menu is clicked
1950 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1951 * @param {Roo.EventObject} e
1955 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1958 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1962 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1965 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1967 registerMenu : true,
1969 menuItems :false, // stores the menu items..
1979 getChildContainer : function() {
1983 getAutoCreate : function(){
1985 //if (['right'].indexOf(this.align)!==-1) {
1986 // cfg.cn[1].cls += ' pull-right'
1992 cls : 'dropdown-menu' ,
1993 style : 'z-index:1000'
1997 if (this.type === 'submenu') {
1998 cfg.cls = 'submenu active';
2000 if (this.type === 'treeview') {
2001 cfg.cls = 'treeview-menu';
2006 initEvents : function() {
2008 // Roo.log("ADD event");
2009 // Roo.log(this.triggerEl.dom);
2011 this.triggerEl.on('click', this.onTriggerClick, this);
2013 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2015 this.triggerEl.addClass('dropdown-toggle');
2018 this.el.on('touchstart' , this.onTouch, this);
2020 this.el.on('click' , this.onClick, this);
2022 this.el.on("mouseover", this.onMouseOver, this);
2023 this.el.on("mouseout", this.onMouseOut, this);
2027 findTargetItem : function(e)
2029 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2033 //Roo.log(t); Roo.log(t.id);
2035 //Roo.log(this.menuitems);
2036 return this.menuitems.get(t.id);
2038 //return this.items.get(t.menuItemId);
2044 onTouch : function(e)
2046 Roo.log("menu.onTouch");
2047 //e.stopEvent(); this make the user popdown broken
2051 onClick : function(e)
2053 Roo.log("menu.onClick");
2055 var t = this.findTargetItem(e);
2056 if(!t || t.isContainer){
2061 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2062 if(t == this.activeItem && t.shouldDeactivate(e)){
2063 this.activeItem.deactivate();
2064 delete this.activeItem;
2068 this.setActiveItem(t, true);
2076 Roo.log('pass click event');
2080 this.fireEvent("click", this, t, e);
2084 (function() { _this.hide(); }).defer(500);
2087 onMouseOver : function(e){
2088 var t = this.findTargetItem(e);
2091 // if(t.canActivate && !t.disabled){
2092 // this.setActiveItem(t, true);
2096 this.fireEvent("mouseover", this, e, t);
2098 isVisible : function(){
2099 return !this.hidden;
2101 onMouseOut : function(e){
2102 var t = this.findTargetItem(e);
2105 // if(t == this.activeItem && t.shouldDeactivate(e)){
2106 // this.activeItem.deactivate();
2107 // delete this.activeItem;
2110 this.fireEvent("mouseout", this, e, t);
2115 * Displays this menu relative to another element
2116 * @param {String/HTMLElement/Roo.Element} element The element to align to
2117 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2118 * the element (defaults to this.defaultAlign)
2119 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2121 show : function(el, pos, parentMenu){
2122 this.parentMenu = parentMenu;
2126 this.fireEvent("beforeshow", this);
2127 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2130 * Displays this menu at a specific xy position
2131 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2132 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2134 showAt : function(xy, parentMenu, /* private: */_e){
2135 this.parentMenu = parentMenu;
2140 this.fireEvent("beforeshow", this);
2141 //xy = this.el.adjustForConstraints(xy);
2145 this.hideMenuItems();
2146 this.hidden = false;
2147 this.triggerEl.addClass('open');
2149 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2150 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2153 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2158 this.fireEvent("show", this);
2164 this.doFocus.defer(50, this);
2168 doFocus : function(){
2170 this.focusEl.focus();
2175 * Hides this menu and optionally all parent menus
2176 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2178 hide : function(deep)
2181 this.hideMenuItems();
2182 if(this.el && this.isVisible()){
2183 this.fireEvent("beforehide", this);
2184 if(this.activeItem){
2185 this.activeItem.deactivate();
2186 this.activeItem = null;
2188 this.triggerEl.removeClass('open');;
2190 this.fireEvent("hide", this);
2192 if(deep === true && this.parentMenu){
2193 this.parentMenu.hide(true);
2197 onTriggerClick : function(e)
2199 Roo.log('trigger click');
2201 var target = e.getTarget();
2203 Roo.log(target.nodeName.toLowerCase());
2205 if(target.nodeName.toLowerCase() === 'i'){
2211 onTriggerPress : function(e)
2213 Roo.log('trigger press');
2214 //Roo.log(e.getTarget());
2215 // Roo.log(this.triggerEl.dom);
2217 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2218 var pel = Roo.get(e.getTarget());
2219 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2220 Roo.log('is treeview or dropdown?');
2224 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2228 if (this.isVisible()) {
2233 this.show(this.triggerEl, false, false);
2236 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2243 hideMenuItems : function()
2245 Roo.log("hide Menu Items");
2249 //$(backdrop).remove()
2250 this.el.select('.open',true).each(function(aa) {
2252 aa.removeClass('open');
2253 //var parent = getParent($(this))
2254 //var relatedTarget = { relatedTarget: this }
2256 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2257 //if (e.isDefaultPrevented()) return
2258 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2261 addxtypeChild : function (tree, cntr) {
2262 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2264 this.menuitems.add(comp);
2285 * @class Roo.bootstrap.MenuItem
2286 * @extends Roo.bootstrap.Component
2287 * Bootstrap MenuItem class
2288 * @cfg {String} html the menu label
2289 * @cfg {String} href the link
2290 * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2291 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2292 * @cfg {Boolean} active used on sidebars to highlight active itesm
2293 * @cfg {String} fa favicon to show on left of menu item.
2294 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2298 * Create a new MenuItem
2299 * @param {Object} config The config object
2303 Roo.bootstrap.MenuItem = function(config){
2304 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2309 * The raw click event for the entire grid.
2310 * @param {Roo.bootstrap.MenuItem} this
2311 * @param {Roo.EventObject} e
2317 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2321 preventDefault: true,
2322 isContainer : false,
2326 getAutoCreate : function(){
2328 if(this.isContainer){
2331 cls: 'dropdown-menu-item'
2345 if (this.fa !== false) {
2348 cls : 'fa fa-' + this.fa
2357 cls: 'dropdown-menu-item',
2360 if (this.parent().type == 'treeview') {
2361 cfg.cls = 'treeview-menu';
2364 cfg.cls += ' active';
2369 anc.href = this.href || cfg.cn[0].href ;
2370 ctag.html = this.html || cfg.cn[0].html ;
2374 initEvents: function()
2376 if (this.parent().type == 'treeview') {
2377 this.el.select('a').on('click', this.onClick, this);
2380 this.menu.parentType = this.xtype;
2381 this.menu.triggerEl = this.el;
2382 this.menu = this.addxtype(Roo.apply({}, this.menu));
2386 onClick : function(e)
2388 Roo.log('item on click ');
2389 //if(this.preventDefault){
2390 // e.preventDefault();
2392 //this.parent().hideMenuItems();
2394 this.fireEvent('click', this, e);
2413 * @class Roo.bootstrap.MenuSeparator
2414 * @extends Roo.bootstrap.Component
2415 * Bootstrap MenuSeparator class
2418 * Create a new MenuItem
2419 * @param {Object} config The config object
2423 Roo.bootstrap.MenuSeparator = function(config){
2424 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2427 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2429 getAutoCreate : function(){
2448 * @class Roo.bootstrap.Modal
2449 * @extends Roo.bootstrap.Component
2450 * Bootstrap Modal class
2451 * @cfg {String} title Title of dialog
2452 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2453 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2454 * @cfg {Boolean} specificTitle default false
2455 * @cfg {Array} buttons Array of buttons or standard button set..
2456 * @cfg {String} buttonPosition (left|right|center) default right
2457 * @cfg {Boolean} animate default true
2458 * @cfg {Boolean} allow_close default true
2459 * @cfg {Boolean} fitwindow default true
2460 * @cfg {String} size (sm|lg) default empty
2464 * Create a new Modal Dialog
2465 * @param {Object} config The config object
2468 Roo.bootstrap.Modal = function(config){
2469 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2474 * The raw btnclick event for the button
2475 * @param {Roo.EventObject} e
2479 this.buttons = this.buttons || [];
2482 this.tmpl = Roo.factory(this.tmpl);
2487 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2489 title : 'test dialog',
2499 specificTitle: false,
2501 buttonPosition: 'right',
2520 onRender : function(ct, position)
2522 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2525 var cfg = Roo.apply({}, this.getAutoCreate());
2528 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2530 //if (!cfg.name.length) {
2534 cfg.cls += ' ' + this.cls;
2537 cfg.style = this.style;
2539 this.el = Roo.get(document.body).createChild(cfg, position);
2541 //var type = this.el.dom.type;
2544 if(this.tabIndex !== undefined){
2545 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2548 this.dialogEl = this.el.select('.modal-dialog',true).first();
2549 this.bodyEl = this.el.select('.modal-body',true).first();
2550 this.closeEl = this.el.select('.modal-header .close', true).first();
2551 this.footerEl = this.el.select('.modal-footer',true).first();
2552 this.titleEl = this.el.select('.modal-title',true).first();
2556 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2557 this.maskEl.enableDisplayMode("block");
2559 //this.el.addClass("x-dlg-modal");
2561 if (this.buttons.length) {
2562 Roo.each(this.buttons, function(bb) {
2563 var b = Roo.apply({}, bb);
2564 b.xns = b.xns || Roo.bootstrap;
2565 b.xtype = b.xtype || 'Button';
2566 if (typeof(b.listeners) == 'undefined') {
2567 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2570 var btn = Roo.factory(b);
2572 btn.render(this.el.select('.modal-footer div').first());
2576 // render the children.
2579 if(typeof(this.items) != 'undefined'){
2580 var items = this.items;
2583 for(var i =0;i < items.length;i++) {
2584 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2588 this.items = nitems;
2590 // where are these used - they used to be body/close/footer
2594 //this.el.addClass([this.fieldClass, this.cls]);
2598 getAutoCreate : function(){
2603 html : this.html || ''
2608 cls : 'modal-title',
2612 if(this.specificTitle){
2618 if (this.allow_close) {
2630 if(this.size.length){
2631 size = 'modal-' + this.size;
2636 style : 'display: none',
2639 cls: "modal-dialog " + size,
2642 cls : "modal-content",
2645 cls : 'modal-header',
2650 cls : 'modal-footer',
2654 cls: 'btn-' + this.buttonPosition
2671 modal.cls += ' fade';
2677 getChildContainer : function() {
2682 getButtonContainer : function() {
2683 return this.el.select('.modal-footer div',true).first();
2686 initEvents : function()
2688 if (this.allow_close) {
2689 this.closeEl.on('click', this.hide, this);
2691 Roo.EventManager.onWindowResize(this.resize, this, true);
2698 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2699 if (this.fitwindow) {
2700 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2701 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 30;
2706 setSize : function(w,h)
2716 if (!this.rendered) {
2720 this.el.setStyle('display', 'block');
2722 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2725 this.el.addClass('in');
2728 this.el.addClass('in');
2732 // not sure how we can show data in here..
2734 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2737 Roo.get(document.body).addClass("x-body-masked");
2738 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2740 this.el.setStyle('zIndex', '10001');
2742 this.fireEvent('show', this);
2743 this.items.forEach(function(e) {
2744 e.layout ? e.layout() : false;
2755 Roo.get(document.body).removeClass("x-body-masked");
2756 this.el.removeClass('in');
2757 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2759 if(this.animate){ // why
2761 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2763 this.el.setStyle('display', 'none');
2766 this.fireEvent('hide', this);
2769 addButton : function(str, cb)
2773 var b = Roo.apply({}, { html : str } );
2774 b.xns = b.xns || Roo.bootstrap;
2775 b.xtype = b.xtype || 'Button';
2776 if (typeof(b.listeners) == 'undefined') {
2777 b.listeners = { click : cb.createDelegate(this) };
2780 var btn = Roo.factory(b);
2782 btn.render(this.el.select('.modal-footer div').first());
2788 setDefaultButton : function(btn)
2790 //this.el.select('.modal-footer').()
2794 resizeTo: function(w,h)
2798 this.dialogEl.setWidth(w);
2799 if (this.diff === false) {
2800 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2803 this.bodyEl.setHeight(h-this.diff);
2807 setContentSize : function(w, h)
2811 onButtonClick: function(btn,e)
2814 this.fireEvent('btnclick', btn.name, e);
2817 * Set the title of the Dialog
2818 * @param {String} str new Title
2820 setTitle: function(str) {
2821 this.titleEl.dom.innerHTML = str;
2824 * Set the body of the Dialog
2825 * @param {String} str new Title
2827 setBody: function(str) {
2828 this.bodyEl.dom.innerHTML = str;
2831 * Set the body of the Dialog using the template
2832 * @param {Obj} data - apply this data to the template and replace the body contents.
2834 applyBody: function(obj)
2837 Roo.log("Error - using apply Body without a template");
2840 this.tmpl.overwrite(this.bodyEl, obj);
2846 Roo.apply(Roo.bootstrap.Modal, {
2848 * Button config that displays a single OK button
2857 * Button config that displays Yes and No buttons
2873 * Button config that displays OK and Cancel buttons
2888 * Button config that displays Yes, No and Cancel buttons
2911 * messagebox - can be used as a replace
2915 * @class Roo.MessageBox
2916 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2920 Roo.Msg.alert('Status', 'Changes saved successfully.');
2922 // Prompt for user data:
2923 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2925 // process text value...
2929 // Show a dialog using config options:
2931 title:'Save Changes?',
2932 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2933 buttons: Roo.Msg.YESNOCANCEL,
2940 Roo.bootstrap.MessageBox = function(){
2941 var dlg, opt, mask, waitTimer;
2942 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2943 var buttons, activeTextEl, bwidth;
2947 var handleButton = function(button){
2949 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2953 var handleHide = function(){
2955 dlg.el.removeClass(opt.cls);
2958 // Roo.TaskMgr.stop(waitTimer);
2959 // waitTimer = null;
2964 var updateButtons = function(b){
2967 buttons["ok"].hide();
2968 buttons["cancel"].hide();
2969 buttons["yes"].hide();
2970 buttons["no"].hide();
2971 //dlg.footer.dom.style.display = 'none';
2974 dlg.footerEl.dom.style.display = '';
2975 for(var k in buttons){
2976 if(typeof buttons[k] != "function"){
2979 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2980 width += buttons[k].el.getWidth()+15;
2990 var handleEsc = function(d, k, e){
2991 if(opt && opt.closable !== false){
3001 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3002 * @return {Roo.BasicDialog} The BasicDialog element
3004 getDialog : function(){
3006 dlg = new Roo.bootstrap.Modal( {
3009 //constraintoviewport:false,
3011 //collapsible : false,
3016 //buttonAlign:"center",
3017 closeClick : function(){
3018 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3021 handleButton("cancel");
3026 dlg.on("hide", handleHide);
3028 //dlg.addKeyListener(27, handleEsc);
3030 this.buttons = buttons;
3031 var bt = this.buttonText;
3032 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3033 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3034 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3035 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3037 bodyEl = dlg.bodyEl.createChild({
3039 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3040 '<textarea class="roo-mb-textarea"></textarea>' +
3041 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3043 msgEl = bodyEl.dom.firstChild;
3044 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3045 textboxEl.enableDisplayMode();
3046 textboxEl.addKeyListener([10,13], function(){
3047 if(dlg.isVisible() && opt && opt.buttons){
3050 }else if(opt.buttons.yes){
3051 handleButton("yes");
3055 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3056 textareaEl.enableDisplayMode();
3057 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3058 progressEl.enableDisplayMode();
3059 var pf = progressEl.dom.firstChild;
3061 pp = Roo.get(pf.firstChild);
3062 pp.setHeight(pf.offsetHeight);
3070 * Updates the message box body text
3071 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3072 * the XHTML-compliant non-breaking space character '&#160;')
3073 * @return {Roo.MessageBox} This message box
3075 updateText : function(text){
3076 if(!dlg.isVisible() && !opt.width){
3077 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3079 msgEl.innerHTML = text || ' ';
3081 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3082 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3084 Math.min(opt.width || cw , this.maxWidth),
3085 Math.max(opt.minWidth || this.minWidth, bwidth)
3088 activeTextEl.setWidth(w);
3090 if(dlg.isVisible()){
3091 dlg.fixedcenter = false;
3093 // to big, make it scroll. = But as usual stupid IE does not support
3096 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3097 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3098 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3100 bodyEl.dom.style.height = '';
3101 bodyEl.dom.style.overflowY = '';
3104 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3106 bodyEl.dom.style.overflowX = '';
3109 dlg.setContentSize(w, bodyEl.getHeight());
3110 if(dlg.isVisible()){
3111 dlg.fixedcenter = true;
3117 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3118 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3119 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3120 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3121 * @return {Roo.MessageBox} This message box
3123 updateProgress : function(value, text){
3125 this.updateText(text);
3127 if (pp) { // weird bug on my firefox - for some reason this is not defined
3128 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3134 * Returns true if the message box is currently displayed
3135 * @return {Boolean} True if the message box is visible, else false
3137 isVisible : function(){
3138 return dlg && dlg.isVisible();
3142 * Hides the message box if it is displayed
3145 if(this.isVisible()){
3151 * Displays a new message box, or reinitializes an existing message box, based on the config options
3152 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3153 * The following config object properties are supported:
3155 Property Type Description
3156 ---------- --------------- ------------------------------------------------------------------------------------
3157 animEl String/Element An id or Element from which the message box should animate as it opens and
3158 closes (defaults to undefined)
3159 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3160 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3161 closable Boolean False to hide the top-right close button (defaults to true). Note that
3162 progress and wait dialogs will ignore this property and always hide the
3163 close button as they can only be closed programmatically.
3164 cls String A custom CSS class to apply to the message box element
3165 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3166 displayed (defaults to 75)
3167 fn Function A callback function to execute after closing the dialog. The arguments to the
3168 function will be btn (the name of the button that was clicked, if applicable,
3169 e.g. "ok"), and text (the value of the active text field, if applicable).
3170 Progress and wait dialogs will ignore this option since they do not respond to
3171 user actions and can only be closed programmatically, so any required function
3172 should be called by the same code after it closes the dialog.
3173 icon String A CSS class that provides a background image to be used as an icon for
3174 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3175 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3176 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3177 modal Boolean False to allow user interaction with the page while the message box is
3178 displayed (defaults to true)
3179 msg String A string that will replace the existing message box body text (defaults
3180 to the XHTML-compliant non-breaking space character ' ')
3181 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3182 progress Boolean True to display a progress bar (defaults to false)
3183 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3184 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3185 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3186 title String The title text
3187 value String The string value to set into the active textbox element if displayed
3188 wait Boolean True to display a progress bar (defaults to false)
3189 width Number The width of the dialog in pixels
3196 msg: 'Please enter your address:',
3198 buttons: Roo.MessageBox.OKCANCEL,
3201 animEl: 'addAddressBtn'
3204 * @param {Object} config Configuration options
3205 * @return {Roo.MessageBox} This message box
3207 show : function(options)
3210 // this causes nightmares if you show one dialog after another
3211 // especially on callbacks..
3213 if(this.isVisible()){
3216 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3217 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3218 Roo.log("New Dialog Message:" + options.msg )
3219 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3220 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3223 var d = this.getDialog();
3225 d.setTitle(opt.title || " ");
3226 d.closeEl.setDisplayed(opt.closable !== false);
3227 activeTextEl = textboxEl;
3228 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3233 textareaEl.setHeight(typeof opt.multiline == "number" ?
3234 opt.multiline : this.defaultTextHeight);
3235 activeTextEl = textareaEl;
3244 progressEl.setDisplayed(opt.progress === true);
3245 this.updateProgress(0);
3246 activeTextEl.dom.value = opt.value || "";
3248 dlg.setDefaultButton(activeTextEl);
3250 var bs = opt.buttons;
3254 }else if(bs && bs.yes){
3255 db = buttons["yes"];
3257 dlg.setDefaultButton(db);
3259 bwidth = updateButtons(opt.buttons);
3260 this.updateText(opt.msg);
3262 d.el.addClass(opt.cls);
3264 d.proxyDrag = opt.proxyDrag === true;
3265 d.modal = opt.modal !== false;
3266 d.mask = opt.modal !== false ? mask : false;
3268 // force it to the end of the z-index stack so it gets a cursor in FF
3269 document.body.appendChild(dlg.el.dom);
3270 d.animateTarget = null;
3271 d.show(options.animEl);
3277 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3278 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3279 * and closing the message box when the process is complete.
3280 * @param {String} title The title bar text
3281 * @param {String} msg The message box body text
3282 * @return {Roo.MessageBox} This message box
3284 progress : function(title, msg){
3291 minWidth: this.minProgressWidth,
3298 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3299 * If a callback function is passed it will be called after the user clicks the button, and the
3300 * id of the button that was clicked will be passed as the only parameter to the callback
3301 * (could also be the top-right close button).
3302 * @param {String} title The title bar text
3303 * @param {String} msg The message box body text
3304 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3305 * @param {Object} scope (optional) The scope of the callback function
3306 * @return {Roo.MessageBox} This message box
3308 alert : function(title, msg, fn, scope){
3321 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3322 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3323 * You are responsible for closing the message box when the process is complete.
3324 * @param {String} msg The message box body text
3325 * @param {String} title (optional) The title bar text
3326 * @return {Roo.MessageBox} This message box
3328 wait : function(msg, title){
3339 waitTimer = Roo.TaskMgr.start({
3341 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3349 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3350 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3351 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3352 * @param {String} title The title bar text
3353 * @param {String} msg The message box body text
3354 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3355 * @param {Object} scope (optional) The scope of the callback function
3356 * @return {Roo.MessageBox} This message box
3358 confirm : function(title, msg, fn, scope){
3362 buttons: this.YESNO,
3371 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3372 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3373 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3374 * (could also be the top-right close button) and the text that was entered will be passed as the two
3375 * parameters to the callback.
3376 * @param {String} title The title bar text
3377 * @param {String} msg The message box body text
3378 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3379 * @param {Object} scope (optional) The scope of the callback function
3380 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3381 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3382 * @return {Roo.MessageBox} This message box
3384 prompt : function(title, msg, fn, scope, multiline){
3388 buttons: this.OKCANCEL,
3393 multiline: multiline,
3400 * Button config that displays a single OK button
3405 * Button config that displays Yes and No buttons
3408 YESNO : {yes:true, no:true},
3410 * Button config that displays OK and Cancel buttons
3413 OKCANCEL : {ok:true, cancel:true},
3415 * Button config that displays Yes, No and Cancel buttons
3418 YESNOCANCEL : {yes:true, no:true, cancel:true},
3421 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3424 defaultTextHeight : 75,
3426 * The maximum width in pixels of the message box (defaults to 600)
3431 * The minimum width in pixels of the message box (defaults to 100)
3436 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3437 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3440 minProgressWidth : 250,
3442 * An object containing the default button text strings that can be overriden for localized language support.
3443 * Supported properties are: ok, cancel, yes and no.
3444 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3457 * Shorthand for {@link Roo.MessageBox}
3459 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3460 Roo.Msg = Roo.Msg || Roo.MessageBox;
3469 * @class Roo.bootstrap.Navbar
3470 * @extends Roo.bootstrap.Component
3471 * Bootstrap Navbar class
3474 * Create a new Navbar
3475 * @param {Object} config The config object
3479 Roo.bootstrap.Navbar = function(config){
3480 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3484 * @event beforetoggle
3485 * Fire before toggle the menu
3486 * @param {Roo.EventObject} e
3488 "beforetoggle" : true
3492 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3501 getAutoCreate : function(){
3504 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3508 initEvents :function ()
3510 //Roo.log(this.el.select('.navbar-toggle',true));
3511 this.el.select('.navbar-toggle',true).on('click', function() {
3512 if(this.fireEvent('beforetoggle', this) !== false){
3513 this.el.select('.navbar-collapse',true).toggleClass('in');
3523 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3525 var size = this.el.getSize();
3526 this.maskEl.setSize(size.width, size.height);
3527 this.maskEl.enableDisplayMode("block");
3536 getChildContainer : function()
3538 if (this.el.select('.collapse').getCount()) {
3539 return this.el.select('.collapse',true).first();
3572 * @class Roo.bootstrap.NavSimplebar
3573 * @extends Roo.bootstrap.Navbar
3574 * Bootstrap Sidebar class
3576 * @cfg {Boolean} inverse is inverted color
3578 * @cfg {String} type (nav | pills | tabs)
3579 * @cfg {Boolean} arrangement stacked | justified
3580 * @cfg {String} align (left | right) alignment
3582 * @cfg {Boolean} main (true|false) main nav bar? default false
3583 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3585 * @cfg {String} tag (header|footer|nav|div) default is nav
3591 * Create a new Sidebar
3592 * @param {Object} config The config object
3596 Roo.bootstrap.NavSimplebar = function(config){
3597 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3600 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3616 getAutoCreate : function(){
3620 tag : this.tag || 'div',
3633 this.type = this.type || 'nav';
3634 if (['tabs','pills'].indexOf(this.type)!==-1) {
3635 cfg.cn[0].cls += ' nav-' + this.type
3639 if (this.type!=='nav') {
3640 Roo.log('nav type must be nav/tabs/pills')
3642 cfg.cn[0].cls += ' navbar-nav'
3648 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3649 cfg.cn[0].cls += ' nav-' + this.arrangement;
3653 if (this.align === 'right') {
3654 cfg.cn[0].cls += ' navbar-right';
3658 cfg.cls += ' navbar-inverse';
3685 * @class Roo.bootstrap.NavHeaderbar
3686 * @extends Roo.bootstrap.NavSimplebar
3687 * Bootstrap Sidebar class
3689 * @cfg {String} brand what is brand
3690 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3691 * @cfg {String} brand_href href of the brand
3692 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3693 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3694 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3695 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3698 * Create a new Sidebar
3699 * @param {Object} config The config object
3703 Roo.bootstrap.NavHeaderbar = function(config){
3704 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3708 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3715 desktopCenter : false,
3718 getAutoCreate : function(){
3721 tag: this.nav || 'nav',
3728 if (this.desktopCenter) {
3729 cn.push({cls : 'container', cn : []});
3736 cls: 'navbar-header',
3741 cls: 'navbar-toggle',
3742 'data-toggle': 'collapse',
3747 html: 'Toggle navigation'
3769 cls: 'collapse navbar-collapse',
3773 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3775 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3776 cfg.cls += ' navbar-' + this.position;
3778 // tag can override this..
3780 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3783 if (this.brand !== '') {
3786 href: this.brand_href ? this.brand_href : '#',
3787 cls: 'navbar-brand',
3795 cfg.cls += ' main-nav';
3803 getHeaderChildContainer : function()
3805 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3806 return this.el.select('.navbar-header',true).first();
3809 return this.getChildContainer();
3813 initEvents : function()
3815 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3817 if (this.autohide) {
3822 Roo.get(document).on('scroll',function(e) {
3823 var ns = Roo.get(document).getScroll().top;
3824 var os = prevScroll;
3828 ft.removeClass('slideDown');
3829 ft.addClass('slideUp');
3832 ft.removeClass('slideUp');
3833 ft.addClass('slideDown');
3854 * @class Roo.bootstrap.NavSidebar
3855 * @extends Roo.bootstrap.Navbar
3856 * Bootstrap Sidebar class
3859 * Create a new Sidebar
3860 * @param {Object} config The config object
3864 Roo.bootstrap.NavSidebar = function(config){
3865 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3868 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3870 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3872 getAutoCreate : function(){
3877 cls: 'sidebar sidebar-nav'
3899 * @class Roo.bootstrap.NavGroup
3900 * @extends Roo.bootstrap.Component
3901 * Bootstrap NavGroup class
3902 * @cfg {String} align (left|right)
3903 * @cfg {Boolean} inverse
3904 * @cfg {String} type (nav|pills|tab) default nav
3905 * @cfg {String} navId - reference Id for navbar.
3909 * Create a new nav group
3910 * @param {Object} config The config object
3913 Roo.bootstrap.NavGroup = function(config){
3914 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3917 Roo.bootstrap.NavGroup.register(this);
3921 * Fires when the active item changes
3922 * @param {Roo.bootstrap.NavGroup} this
3923 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3924 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3931 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3942 getAutoCreate : function()
3944 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3951 if (['tabs','pills'].indexOf(this.type)!==-1) {
3952 cfg.cls += ' nav-' + this.type
3954 if (this.type!=='nav') {
3955 Roo.log('nav type must be nav/tabs/pills')
3957 cfg.cls += ' navbar-nav'
3960 if (this.parent().sidebar) {
3963 cls: 'dashboard-menu sidebar-menu'
3969 if (this.form === true) {
3975 if (this.align === 'right') {
3976 cfg.cls += ' navbar-right';
3978 cfg.cls += ' navbar-left';
3982 if (this.align === 'right') {
3983 cfg.cls += ' navbar-right';
3987 cfg.cls += ' navbar-inverse';
3995 * sets the active Navigation item
3996 * @param {Roo.bootstrap.NavItem} the new current navitem
3998 setActiveItem : function(item)
4001 Roo.each(this.navItems, function(v){
4006 v.setActive(false, true);
4013 item.setActive(true, true);
4014 this.fireEvent('changed', this, item, prev);
4019 * gets the active Navigation item
4020 * @return {Roo.bootstrap.NavItem} the current navitem
4022 getActive : function()
4026 Roo.each(this.navItems, function(v){
4037 indexOfNav : function()
4041 Roo.each(this.navItems, function(v,i){
4052 * adds a Navigation item
4053 * @param {Roo.bootstrap.NavItem} the navitem to add
4055 addItem : function(cfg)
4057 var cn = new Roo.bootstrap.NavItem(cfg);
4059 cn.parentId = this.id;
4060 cn.onRender(this.el, null);
4064 * register a Navigation item
4065 * @param {Roo.bootstrap.NavItem} the navitem to add
4067 register : function(item)
4069 this.navItems.push( item);
4070 item.navId = this.navId;
4075 * clear all the Navigation item
4078 clearAll : function()
4081 this.el.dom.innerHTML = '';
4084 getNavItem: function(tabId)
4087 Roo.each(this.navItems, function(e) {
4088 if (e.tabId == tabId) {
4098 setActiveNext : function()
4100 var i = this.indexOfNav(this.getActive());
4101 if (i > this.navItems.length) {
4104 this.setActiveItem(this.navItems[i+1]);
4106 setActivePrev : function()
4108 var i = this.indexOfNav(this.getActive());
4112 this.setActiveItem(this.navItems[i-1]);
4114 clearWasActive : function(except) {
4115 Roo.each(this.navItems, function(e) {
4116 if (e.tabId != except.tabId && e.was_active) {
4117 e.was_active = false;
4124 getWasActive : function ()
4127 Roo.each(this.navItems, function(e) {
4142 Roo.apply(Roo.bootstrap.NavGroup, {
4146 * register a Navigation Group
4147 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4149 register : function(navgrp)
4151 this.groups[navgrp.navId] = navgrp;
4155 * fetch a Navigation Group based on the navigation ID
4156 * @param {string} the navgroup to add
4157 * @returns {Roo.bootstrap.NavGroup} the navgroup
4159 get: function(navId) {
4160 if (typeof(this.groups[navId]) == 'undefined') {
4162 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4164 return this.groups[navId] ;
4179 * @class Roo.bootstrap.NavItem
4180 * @extends Roo.bootstrap.Component
4181 * Bootstrap Navbar.NavItem class
4182 * @cfg {String} href link to
4183 * @cfg {String} html content of button
4184 * @cfg {String} badge text inside badge
4185 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4186 * @cfg {String} glyphicon name of glyphicon
4187 * @cfg {String} icon name of font awesome icon
4188 * @cfg {Boolean} active Is item active
4189 * @cfg {Boolean} disabled Is item disabled
4191 * @cfg {Boolean} preventDefault (true | false) default false
4192 * @cfg {String} tabId the tab that this item activates.
4193 * @cfg {String} tagtype (a|span) render as a href or span?
4194 * @cfg {Boolean} animateRef (true|false) link to element default false
4197 * Create a new Navbar Item
4198 * @param {Object} config The config object
4200 Roo.bootstrap.NavItem = function(config){
4201 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4206 * The raw click event for the entire grid.
4207 * @param {Roo.EventObject} e
4212 * Fires when the active item active state changes
4213 * @param {Roo.bootstrap.NavItem} this
4214 * @param {boolean} state the new state
4220 * Fires when scroll to element
4221 * @param {Roo.bootstrap.NavItem} this
4222 * @param {Object} options
4223 * @param {Roo.EventObject} e
4231 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4239 preventDefault : false,
4246 getAutoCreate : function(){
4255 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4257 if (this.disabled) {
4258 cfg.cls += ' disabled';
4261 if (this.href || this.html || this.glyphicon || this.icon) {
4265 href : this.href || "#",
4266 html: this.html || ''
4271 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4274 if(this.glyphicon) {
4275 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4280 cfg.cn[0].html += " <span class='caret'></span>";
4284 if (this.badge !== '') {
4286 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4294 initEvents: function()
4296 if (typeof (this.menu) != 'undefined') {
4297 this.menu.parentType = this.xtype;
4298 this.menu.triggerEl = this.el;
4299 this.menu = this.addxtype(Roo.apply({}, this.menu));
4302 this.el.select('a',true).on('click', this.onClick, this);
4304 if(this.tagtype == 'span'){
4305 this.el.select('span',true).on('click', this.onClick, this);
4308 // at this point parent should be available..
4309 this.parent().register(this);
4312 onClick : function(e)
4314 if (e.getTarget('.dropdown-menu-item')) {
4315 // did you click on a menu itemm.... - then don't trigger onclick..
4320 this.preventDefault ||
4323 Roo.log("NavItem - prevent Default?");
4327 if (this.disabled) {
4331 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4332 if (tg && tg.transition) {
4333 Roo.log("waiting for the transitionend");
4339 //Roo.log("fire event clicked");
4340 if(this.fireEvent('click', this, e) === false){
4344 if(this.tagtype == 'span'){
4348 //Roo.log(this.href);
4349 var ael = this.el.select('a',true).first();
4352 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4353 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4354 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4355 return; // ignore... - it's a 'hash' to another page.
4357 Roo.log("NavItem - prevent Default?");
4359 this.scrollToElement(e);
4363 var p = this.parent();
4365 if (['tabs','pills'].indexOf(p.type)!==-1) {
4366 if (typeof(p.setActiveItem) !== 'undefined') {
4367 p.setActiveItem(this);
4371 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4372 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4373 // remove the collapsed menu expand...
4374 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4378 isActive: function () {
4381 setActive : function(state, fire, is_was_active)
4383 if (this.active && !state && this.navId) {
4384 this.was_active = true;
4385 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4387 nv.clearWasActive(this);
4391 this.active = state;
4394 this.el.removeClass('active');
4395 } else if (!this.el.hasClass('active')) {
4396 this.el.addClass('active');
4399 this.fireEvent('changed', this, state);
4402 // show a panel if it's registered and related..
4404 if (!this.navId || !this.tabId || !state || is_was_active) {
4408 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4412 var pan = tg.getPanelByName(this.tabId);
4416 // if we can not flip to new panel - go back to old nav highlight..
4417 if (false == tg.showPanel(pan)) {
4418 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4420 var onav = nv.getWasActive();
4422 onav.setActive(true, false, true);
4431 // this should not be here...
4432 setDisabled : function(state)
4434 this.disabled = state;
4436 this.el.removeClass('disabled');
4437 } else if (!this.el.hasClass('disabled')) {
4438 this.el.addClass('disabled');
4444 * Fetch the element to display the tooltip on.
4445 * @return {Roo.Element} defaults to this.el
4447 tooltipEl : function()
4449 return this.el.select('' + this.tagtype + '', true).first();
4452 scrollToElement : function(e)
4454 var c = document.body;
4457 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4459 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4460 c = document.documentElement;
4463 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4469 var o = target.calcOffsetsTo(c);
4476 this.fireEvent('scrollto', this, options, e);
4478 Roo.get(c).scrollTo('top', options.value, true);
4491 * <span> icon </span>
4492 * <span> text </span>
4493 * <span>badge </span>
4497 * @class Roo.bootstrap.NavSidebarItem
4498 * @extends Roo.bootstrap.NavItem
4499 * Bootstrap Navbar.NavSidebarItem class
4500 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4501 * {bool} open is the menu open
4503 * Create a new Navbar Button
4504 * @param {Object} config The config object
4506 Roo.bootstrap.NavSidebarItem = function(config){
4507 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4512 * The raw click event for the entire grid.
4513 * @param {Roo.EventObject} e
4518 * Fires when the active item active state changes
4519 * @param {Roo.bootstrap.NavSidebarItem} this
4520 * @param {boolean} state the new state
4528 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4530 badgeWeight : 'default',
4534 getAutoCreate : function(){
4539 href : this.href || '#',
4551 html : this.html || ''
4556 cfg.cls += ' active';
4559 if (this.disabled) {
4560 cfg.cls += ' disabled';
4563 cfg.cls += ' open x-open';
4566 if (this.glyphicon || this.icon) {
4567 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4568 a.cn.push({ tag : 'i', cls : c }) ;
4573 if (this.badge !== '') {
4575 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4579 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4580 a.cls += 'dropdown-toggle treeview' ;
4588 initEvents : function()
4590 if (typeof (this.menu) != 'undefined') {
4591 this.menu.parentType = this.xtype;
4592 this.menu.triggerEl = this.el;
4593 this.menu = this.addxtype(Roo.apply({}, this.menu));
4596 this.el.on('click', this.onClick, this);
4599 if(this.badge !== ''){
4601 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4606 onClick : function(e)
4613 if(this.preventDefault){
4617 this.fireEvent('click', this);
4620 disable : function()
4622 this.setDisabled(true);
4627 this.setDisabled(false);
4630 setDisabled : function(state)
4632 if(this.disabled == state){
4636 this.disabled = state;
4639 this.el.addClass('disabled');
4643 this.el.removeClass('disabled');
4648 setActive : function(state)
4650 if(this.active == state){
4654 this.active = state;
4657 this.el.addClass('active');
4661 this.el.removeClass('active');
4666 isActive: function ()
4671 setBadge : function(str)
4677 this.badgeEl.dom.innerHTML = str;
4694 * @class Roo.bootstrap.Row
4695 * @extends Roo.bootstrap.Component
4696 * Bootstrap Row class (contains columns...)
4700 * @param {Object} config The config object
4703 Roo.bootstrap.Row = function(config){
4704 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4707 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4709 getAutoCreate : function(){
4728 * @class Roo.bootstrap.Element
4729 * @extends Roo.bootstrap.Component
4730 * Bootstrap Element class
4731 * @cfg {String} html contents of the element
4732 * @cfg {String} tag tag of the element
4733 * @cfg {String} cls class of the element
4734 * @cfg {Boolean} preventDefault (true|false) default false
4735 * @cfg {Boolean} clickable (true|false) default false
4738 * Create a new Element
4739 * @param {Object} config The config object
4742 Roo.bootstrap.Element = function(config){
4743 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4749 * When a element is chick
4750 * @param {Roo.bootstrap.Element} this
4751 * @param {Roo.EventObject} e
4757 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4762 preventDefault: false,
4765 getAutoCreate : function(){
4776 initEvents: function()
4778 Roo.bootstrap.Element.superclass.initEvents.call(this);
4781 this.el.on('click', this.onClick, this);
4786 onClick : function(e)
4788 if(this.preventDefault){
4792 this.fireEvent('click', this, e);
4795 getValue : function()
4797 return this.el.dom.innerHTML;
4800 setValue : function(value)
4802 this.el.dom.innerHTML = value;
4817 * @class Roo.bootstrap.Pagination
4818 * @extends Roo.bootstrap.Component
4819 * Bootstrap Pagination class
4820 * @cfg {String} size xs | sm | md | lg
4821 * @cfg {Boolean} inverse false | true
4824 * Create a new Pagination
4825 * @param {Object} config The config object
4828 Roo.bootstrap.Pagination = function(config){
4829 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4832 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4838 getAutoCreate : function(){
4844 cfg.cls += ' inverse';
4850 cfg.cls += " " + this.cls;
4868 * @class Roo.bootstrap.PaginationItem
4869 * @extends Roo.bootstrap.Component
4870 * Bootstrap PaginationItem class
4871 * @cfg {String} html text
4872 * @cfg {String} href the link
4873 * @cfg {Boolean} preventDefault (true | false) default true
4874 * @cfg {Boolean} active (true | false) default false
4875 * @cfg {Boolean} disabled default false
4879 * Create a new PaginationItem
4880 * @param {Object} config The config object
4884 Roo.bootstrap.PaginationItem = function(config){
4885 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4890 * The raw click event for the entire grid.
4891 * @param {Roo.EventObject} e
4897 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4901 preventDefault: true,
4906 getAutoCreate : function(){
4912 href : this.href ? this.href : '#',
4913 html : this.html ? this.html : ''
4923 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4927 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4933 initEvents: function() {
4935 this.el.on('click', this.onClick, this);
4938 onClick : function(e)
4940 Roo.log('PaginationItem on click ');
4941 if(this.preventDefault){
4949 this.fireEvent('click', this, e);
4965 * @class Roo.bootstrap.Slider
4966 * @extends Roo.bootstrap.Component
4967 * Bootstrap Slider class
4970 * Create a new Slider
4971 * @param {Object} config The config object
4974 Roo.bootstrap.Slider = function(config){
4975 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4978 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4980 getAutoCreate : function(){
4984 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4988 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5000 * Ext JS Library 1.1.1
5001 * Copyright(c) 2006-2007, Ext JS, LLC.
5003 * Originally Released Under LGPL - original licence link has changed is not relivant.
5006 * <script type="text/javascript">
5011 * @class Roo.grid.ColumnModel
5012 * @extends Roo.util.Observable
5013 * This is the default implementation of a ColumnModel used by the Grid. It defines
5014 * the columns in the grid.
5017 var colModel = new Roo.grid.ColumnModel([
5018 {header: "Ticker", width: 60, sortable: true, locked: true},
5019 {header: "Company Name", width: 150, sortable: true},
5020 {header: "Market Cap.", width: 100, sortable: true},
5021 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5022 {header: "Employees", width: 100, sortable: true, resizable: false}
5027 * The config options listed for this class are options which may appear in each
5028 * individual column definition.
5029 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5031 * @param {Object} config An Array of column config objects. See this class's
5032 * config objects for details.
5034 Roo.grid.ColumnModel = function(config){
5036 * The config passed into the constructor
5038 this.config = config;
5041 // if no id, create one
5042 // if the column does not have a dataIndex mapping,
5043 // map it to the order it is in the config
5044 for(var i = 0, len = config.length; i < len; i++){
5046 if(typeof c.dataIndex == "undefined"){
5049 if(typeof c.renderer == "string"){
5050 c.renderer = Roo.util.Format[c.renderer];
5052 if(typeof c.id == "undefined"){
5055 if(c.editor && c.editor.xtype){
5056 c.editor = Roo.factory(c.editor, Roo.grid);
5058 if(c.editor && c.editor.isFormField){
5059 c.editor = new Roo.grid.GridEditor(c.editor);
5061 this.lookup[c.id] = c;
5065 * The width of columns which have no width specified (defaults to 100)
5068 this.defaultWidth = 100;
5071 * Default sortable of columns which have no sortable specified (defaults to false)
5074 this.defaultSortable = false;
5078 * @event widthchange
5079 * Fires when the width of a column changes.
5080 * @param {ColumnModel} this
5081 * @param {Number} columnIndex The column index
5082 * @param {Number} newWidth The new width
5084 "widthchange": true,
5086 * @event headerchange
5087 * Fires when the text of a header changes.
5088 * @param {ColumnModel} this
5089 * @param {Number} columnIndex The column index
5090 * @param {Number} newText The new header text
5092 "headerchange": true,
5094 * @event hiddenchange
5095 * Fires when a column is hidden or "unhidden".
5096 * @param {ColumnModel} this
5097 * @param {Number} columnIndex The column index
5098 * @param {Boolean} hidden true if hidden, false otherwise
5100 "hiddenchange": true,
5102 * @event columnmoved
5103 * Fires when a column is moved.
5104 * @param {ColumnModel} this
5105 * @param {Number} oldIndex
5106 * @param {Number} newIndex
5108 "columnmoved" : true,
5110 * @event columlockchange
5111 * Fires when a column's locked state is changed
5112 * @param {ColumnModel} this
5113 * @param {Number} colIndex
5114 * @param {Boolean} locked true if locked
5116 "columnlockchange" : true
5118 Roo.grid.ColumnModel.superclass.constructor.call(this);
5120 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5122 * @cfg {String} header The header text to display in the Grid view.
5125 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5126 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5127 * specified, the column's index is used as an index into the Record's data Array.
5130 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5131 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5134 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5135 * Defaults to the value of the {@link #defaultSortable} property.
5136 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5139 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5142 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5145 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5148 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5151 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5152 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5153 * default renderer uses the raw data value. If an object is returned (bootstrap only)
5154 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5157 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5160 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5163 * @cfg {String} cursor (Optional)
5166 * @cfg {String} tooltip (Optional)
5169 * @cfg {Number} xs (Optional)
5172 * @cfg {Number} sm (Optional)
5175 * @cfg {Number} md (Optional)
5178 * @cfg {Number} lg (Optional)
5181 * Returns the id of the column at the specified index.
5182 * @param {Number} index The column index
5183 * @return {String} the id
5185 getColumnId : function(index){
5186 return this.config[index].id;
5190 * Returns the column for a specified id.
5191 * @param {String} id The column id
5192 * @return {Object} the column
5194 getColumnById : function(id){
5195 return this.lookup[id];
5200 * Returns the column for a specified dataIndex.
5201 * @param {String} dataIndex The column dataIndex
5202 * @return {Object|Boolean} the column or false if not found
5204 getColumnByDataIndex: function(dataIndex){
5205 var index = this.findColumnIndex(dataIndex);
5206 return index > -1 ? this.config[index] : false;
5210 * Returns the index for a specified column id.
5211 * @param {String} id The column id
5212 * @return {Number} the index, or -1 if not found
5214 getIndexById : function(id){
5215 for(var i = 0, len = this.config.length; i < len; i++){
5216 if(this.config[i].id == id){
5224 * Returns the index for a specified column dataIndex.
5225 * @param {String} dataIndex The column dataIndex
5226 * @return {Number} the index, or -1 if not found
5229 findColumnIndex : function(dataIndex){
5230 for(var i = 0, len = this.config.length; i < len; i++){
5231 if(this.config[i].dataIndex == dataIndex){
5239 moveColumn : function(oldIndex, newIndex){
5240 var c = this.config[oldIndex];
5241 this.config.splice(oldIndex, 1);
5242 this.config.splice(newIndex, 0, c);
5243 this.dataMap = null;
5244 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5247 isLocked : function(colIndex){
5248 return this.config[colIndex].locked === true;
5251 setLocked : function(colIndex, value, suppressEvent){
5252 if(this.isLocked(colIndex) == value){
5255 this.config[colIndex].locked = value;
5257 this.fireEvent("columnlockchange", this, colIndex, value);
5261 getTotalLockedWidth : function(){
5263 for(var i = 0; i < this.config.length; i++){
5264 if(this.isLocked(i) && !this.isHidden(i)){
5265 this.totalWidth += this.getColumnWidth(i);
5271 getLockedCount : function(){
5272 for(var i = 0, len = this.config.length; i < len; i++){
5273 if(!this.isLocked(i)){
5278 return this.config.length;
5282 * Returns the number of columns.
5285 getColumnCount : function(visibleOnly){
5286 if(visibleOnly === true){
5288 for(var i = 0, len = this.config.length; i < len; i++){
5289 if(!this.isHidden(i)){
5295 return this.config.length;
5299 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5300 * @param {Function} fn
5301 * @param {Object} scope (optional)
5302 * @return {Array} result
5304 getColumnsBy : function(fn, scope){
5306 for(var i = 0, len = this.config.length; i < len; i++){
5307 var c = this.config[i];
5308 if(fn.call(scope||this, c, i) === true){
5316 * Returns true if the specified column is sortable.
5317 * @param {Number} col The column index
5320 isSortable : function(col){
5321 if(typeof this.config[col].sortable == "undefined"){
5322 return this.defaultSortable;
5324 return this.config[col].sortable;
5328 * Returns the rendering (formatting) function defined for the column.
5329 * @param {Number} col The column index.
5330 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5332 getRenderer : function(col){
5333 if(!this.config[col].renderer){
5334 return Roo.grid.ColumnModel.defaultRenderer;
5336 return this.config[col].renderer;
5340 * Sets the rendering (formatting) function for a column.
5341 * @param {Number} col The column index
5342 * @param {Function} fn The function to use to process the cell's raw data
5343 * to return HTML markup for the grid view. The render function is called with
5344 * the following parameters:<ul>
5345 * <li>Data value.</li>
5346 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5347 * <li>css A CSS style string to apply to the table cell.</li>
5348 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5349 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5350 * <li>Row index</li>
5351 * <li>Column index</li>
5352 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5354 setRenderer : function(col, fn){
5355 this.config[col].renderer = fn;
5359 * Returns the width for the specified column.
5360 * @param {Number} col The column index
5363 getColumnWidth : function(col){
5364 return this.config[col].width * 1 || this.defaultWidth;
5368 * Sets the width for a column.
5369 * @param {Number} col The column index
5370 * @param {Number} width The new width
5372 setColumnWidth : function(col, width, suppressEvent){
5373 this.config[col].width = width;
5374 this.totalWidth = null;
5376 this.fireEvent("widthchange", this, col, width);
5381 * Returns the total width of all columns.
5382 * @param {Boolean} includeHidden True to include hidden column widths
5385 getTotalWidth : function(includeHidden){
5386 if(!this.totalWidth){
5387 this.totalWidth = 0;
5388 for(var i = 0, len = this.config.length; i < len; i++){
5389 if(includeHidden || !this.isHidden(i)){
5390 this.totalWidth += this.getColumnWidth(i);
5394 return this.totalWidth;
5398 * Returns the header for the specified column.
5399 * @param {Number} col The column index
5402 getColumnHeader : function(col){
5403 return this.config[col].header;
5407 * Sets the header for a column.
5408 * @param {Number} col The column index
5409 * @param {String} header The new header
5411 setColumnHeader : function(col, header){
5412 this.config[col].header = header;
5413 this.fireEvent("headerchange", this, col, header);
5417 * Returns the tooltip for the specified column.
5418 * @param {Number} col The column index
5421 getColumnTooltip : function(col){
5422 return this.config[col].tooltip;
5425 * Sets the tooltip for a column.
5426 * @param {Number} col The column index
5427 * @param {String} tooltip The new tooltip
5429 setColumnTooltip : function(col, tooltip){
5430 this.config[col].tooltip = tooltip;
5434 * Returns the dataIndex for the specified column.
5435 * @param {Number} col The column index
5438 getDataIndex : function(col){
5439 return this.config[col].dataIndex;
5443 * Sets the dataIndex for a column.
5444 * @param {Number} col The column index
5445 * @param {Number} dataIndex The new dataIndex
5447 setDataIndex : function(col, dataIndex){
5448 this.config[col].dataIndex = dataIndex;
5454 * Returns true if the cell is editable.
5455 * @param {Number} colIndex The column index
5456 * @param {Number} rowIndex The row index - this is nto actually used..?
5459 isCellEditable : function(colIndex, rowIndex){
5460 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5464 * Returns the editor defined for the cell/column.
5465 * return false or null to disable editing.
5466 * @param {Number} colIndex The column index
5467 * @param {Number} rowIndex The row index
5470 getCellEditor : function(colIndex, rowIndex){
5471 return this.config[colIndex].editor;
5475 * Sets if a column is editable.
5476 * @param {Number} col The column index
5477 * @param {Boolean} editable True if the column is editable
5479 setEditable : function(col, editable){
5480 this.config[col].editable = editable;
5485 * Returns true if the column is hidden.
5486 * @param {Number} colIndex The column index
5489 isHidden : function(colIndex){
5490 return this.config[colIndex].hidden;
5495 * Returns true if the column width cannot be changed
5497 isFixed : function(colIndex){
5498 return this.config[colIndex].fixed;
5502 * Returns true if the column can be resized
5505 isResizable : function(colIndex){
5506 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5509 * Sets if a column is hidden.
5510 * @param {Number} colIndex The column index
5511 * @param {Boolean} hidden True if the column is hidden
5513 setHidden : function(colIndex, hidden){
5514 this.config[colIndex].hidden = hidden;
5515 this.totalWidth = null;
5516 this.fireEvent("hiddenchange", this, colIndex, hidden);
5520 * Sets the editor for a column.
5521 * @param {Number} col The column index
5522 * @param {Object} editor The editor object
5524 setEditor : function(col, editor){
5525 this.config[col].editor = editor;
5529 Roo.grid.ColumnModel.defaultRenderer = function(value){
5530 if(typeof value == "string" && value.length < 1){
5536 // Alias for backwards compatibility
5537 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5540 * Ext JS Library 1.1.1
5541 * Copyright(c) 2006-2007, Ext JS, LLC.
5543 * Originally Released Under LGPL - original licence link has changed is not relivant.
5546 * <script type="text/javascript">
5550 * @class Roo.LoadMask
5551 * A simple utility class for generically masking elements while loading data. If the element being masked has
5552 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5553 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5554 * element's UpdateManager load indicator and will be destroyed after the initial load.
5556 * Create a new LoadMask
5557 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5558 * @param {Object} config The config object
5560 Roo.LoadMask = function(el, config){
5561 this.el = Roo.get(el);
5562 Roo.apply(this, config);
5564 this.store.on('beforeload', this.onBeforeLoad, this);
5565 this.store.on('load', this.onLoad, this);
5566 this.store.on('loadexception', this.onLoadException, this);
5567 this.removeMask = false;
5569 var um = this.el.getUpdateManager();
5570 um.showLoadIndicator = false; // disable the default indicator
5571 um.on('beforeupdate', this.onBeforeLoad, this);
5572 um.on('update', this.onLoad, this);
5573 um.on('failure', this.onLoad, this);
5574 this.removeMask = true;
5578 Roo.LoadMask.prototype = {
5580 * @cfg {Boolean} removeMask
5581 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5582 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5586 * The text to display in a centered loading message box (defaults to 'Loading...')
5590 * @cfg {String} msgCls
5591 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5593 msgCls : 'x-mask-loading',
5596 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5602 * Disables the mask to prevent it from being displayed
5604 disable : function(){
5605 this.disabled = true;
5609 * Enables the mask so that it can be displayed
5611 enable : function(){
5612 this.disabled = false;
5615 onLoadException : function()
5619 if (typeof(arguments[3]) != 'undefined') {
5620 Roo.MessageBox.alert("Error loading",arguments[3]);
5624 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5625 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5634 this.el.unmask(this.removeMask);
5639 this.el.unmask(this.removeMask);
5643 onBeforeLoad : function(){
5645 this.el.mask(this.msg, this.msgCls);
5650 destroy : function(){
5652 this.store.un('beforeload', this.onBeforeLoad, this);
5653 this.store.un('load', this.onLoad, this);
5654 this.store.un('loadexception', this.onLoadException, this);
5656 var um = this.el.getUpdateManager();
5657 um.un('beforeupdate', this.onBeforeLoad, this);
5658 um.un('update', this.onLoad, this);
5659 um.un('failure', this.onLoad, this);
5670 * @class Roo.bootstrap.Table
5671 * @extends Roo.bootstrap.Component
5672 * Bootstrap Table class
5673 * @cfg {String} cls table class
5674 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5675 * @cfg {String} bgcolor Specifies the background color for a table
5676 * @cfg {Number} border Specifies whether the table cells should have borders or not
5677 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5678 * @cfg {Number} cellspacing Specifies the space between cells
5679 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5680 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5681 * @cfg {String} sortable Specifies that the table should be sortable
5682 * @cfg {String} summary Specifies a summary of the content of a table
5683 * @cfg {Number} width Specifies the width of a table
5684 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5686 * @cfg {boolean} striped Should the rows be alternative striped
5687 * @cfg {boolean} bordered Add borders to the table
5688 * @cfg {boolean} hover Add hover highlighting
5689 * @cfg {boolean} condensed Format condensed
5690 * @cfg {boolean} responsive Format condensed
5691 * @cfg {Boolean} loadMask (true|false) default false
5692 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5693 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5694 * @cfg {Boolean} rowSelection (true|false) default false
5695 * @cfg {Boolean} cellSelection (true|false) default false
5696 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5697 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5701 * Create a new Table
5702 * @param {Object} config The config object
5705 Roo.bootstrap.Table = function(config){
5706 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5711 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5712 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5713 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5714 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5718 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5719 this.sm = this.selModel;
5720 this.sm.xmodule = this.xmodule || false;
5722 if (this.cm && typeof(this.cm.config) == 'undefined') {
5723 this.colModel = new Roo.grid.ColumnModel(this.cm);
5724 this.cm = this.colModel;
5725 this.cm.xmodule = this.xmodule || false;
5728 this.store= Roo.factory(this.store, Roo.data);
5729 this.ds = this.store;
5730 this.ds.xmodule = this.xmodule || false;
5733 if (this.footer && this.store) {
5734 this.footer.dataSource = this.ds;
5735 this.footer = Roo.factory(this.footer);
5742 * Fires when a cell is clicked
5743 * @param {Roo.bootstrap.Table} this
5744 * @param {Roo.Element} el
5745 * @param {Number} rowIndex
5746 * @param {Number} columnIndex
5747 * @param {Roo.EventObject} e
5751 * @event celldblclick
5752 * Fires when a cell is double clicked
5753 * @param {Roo.bootstrap.Table} this
5754 * @param {Roo.Element} el
5755 * @param {Number} rowIndex
5756 * @param {Number} columnIndex
5757 * @param {Roo.EventObject} e
5759 "celldblclick" : true,
5762 * Fires when a row is clicked
5763 * @param {Roo.bootstrap.Table} this
5764 * @param {Roo.Element} el
5765 * @param {Number} rowIndex
5766 * @param {Roo.EventObject} e
5770 * @event rowdblclick
5771 * Fires when a row is double clicked
5772 * @param {Roo.bootstrap.Table} this
5773 * @param {Roo.Element} el
5774 * @param {Number} rowIndex
5775 * @param {Roo.EventObject} e
5777 "rowdblclick" : true,
5780 * Fires when a mouseover occur
5781 * @param {Roo.bootstrap.Table} this
5782 * @param {Roo.Element} el
5783 * @param {Number} rowIndex
5784 * @param {Number} columnIndex
5785 * @param {Roo.EventObject} e
5790 * Fires when a mouseout occur
5791 * @param {Roo.bootstrap.Table} this
5792 * @param {Roo.Element} el
5793 * @param {Number} rowIndex
5794 * @param {Number} columnIndex
5795 * @param {Roo.EventObject} e
5800 * Fires when a row is rendered, so you can change add a style to it.
5801 * @param {Roo.bootstrap.Table} this
5802 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5806 * @event rowsrendered
5807 * Fires when all the rows have been rendered
5808 * @param {Roo.bootstrap.Table} this
5810 'rowsrendered' : true,
5812 * @event contextmenu
5813 * The raw contextmenu event for the entire grid.
5814 * @param {Roo.EventObject} e
5816 "contextmenu" : true,
5818 * @event rowcontextmenu
5819 * Fires when a row is right clicked
5820 * @param {Roo.bootstrap.Table} this
5821 * @param {Number} rowIndex
5822 * @param {Roo.EventObject} e
5824 "rowcontextmenu" : true,
5826 * @event cellcontextmenu
5827 * Fires when a cell is right clicked
5828 * @param {Roo.bootstrap.Table} this
5829 * @param {Number} rowIndex
5830 * @param {Number} cellIndex
5831 * @param {Roo.EventObject} e
5833 "cellcontextmenu" : true,
5835 * @event headercontextmenu
5836 * Fires when a header is right clicked
5837 * @param {Roo.bootstrap.Table} this
5838 * @param {Number} columnIndex
5839 * @param {Roo.EventObject} e
5841 "headercontextmenu" : true
5845 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5871 rowSelection : false,
5872 cellSelection : false,
5875 // Roo.Element - the tbody
5877 // Roo.Element - thead element
5880 container: false, // used by gridpanel...
5882 getAutoCreate : function()
5884 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5891 if (this.scrollBody) {
5892 cfg.cls += ' table-body-fixed';
5895 cfg.cls += ' table-striped';
5899 cfg.cls += ' table-hover';
5901 if (this.bordered) {
5902 cfg.cls += ' table-bordered';
5904 if (this.condensed) {
5905 cfg.cls += ' table-condensed';
5907 if (this.responsive) {
5908 cfg.cls += ' table-responsive';
5912 cfg.cls+= ' ' +this.cls;
5915 // this lot should be simplifed...
5918 cfg.align=this.align;
5921 cfg.bgcolor=this.bgcolor;
5924 cfg.border=this.border;
5926 if (this.cellpadding) {
5927 cfg.cellpadding=this.cellpadding;
5929 if (this.cellspacing) {
5930 cfg.cellspacing=this.cellspacing;
5933 cfg.frame=this.frame;
5936 cfg.rules=this.rules;
5938 if (this.sortable) {
5939 cfg.sortable=this.sortable;
5942 cfg.summary=this.summary;
5945 cfg.width=this.width;
5948 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5951 if(this.store || this.cm){
5952 if(this.headerShow){
5953 cfg.cn.push(this.renderHeader());
5956 cfg.cn.push(this.renderBody());
5958 if(this.footerShow){
5959 cfg.cn.push(this.renderFooter());
5961 // where does this come from?
5962 //cfg.cls+= ' TableGrid';
5965 return { cn : [ cfg ] };
5968 initEvents : function()
5970 if(!this.store || !this.cm){
5974 //Roo.log('initEvents with ds!!!!');
5976 this.mainBody = this.el.select('tbody', true).first();
5977 this.mainHead = this.el.select('thead', true).first();
5983 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5984 e.on('click', _this.sort, _this);
5987 this.el.on("click", this.onClick, this);
5988 this.el.on("dblclick", this.onDblClick, this);
5990 // why is this done????? = it breaks dialogs??
5991 //this.parent().el.setStyle('position', 'relative');
5995 this.footer.parentId = this.id;
5996 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5999 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6001 this.store.on('load', this.onLoad, this);
6002 this.store.on('beforeload', this.onBeforeLoad, this);
6003 this.store.on('update', this.onUpdate, this);
6004 this.store.on('add', this.onAdd, this);
6005 this.store.on("clear", this.clear, this);
6007 this.el.on("contextmenu", this.onContextMenu, this);
6009 this.mainBody.on('scroll', this.onBodyScroll, this);
6014 onContextMenu : function(e, t)
6016 this.processEvent("contextmenu", e);
6019 processEvent : function(name, e)
6021 if (name != 'touchstart' ) {
6022 this.fireEvent(name, e);
6025 var t = e.getTarget();
6027 var cell = Roo.get(t);
6033 if(cell.findParent('tfoot', false, true)){
6037 if(cell.findParent('thead', false, true)){
6039 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6040 cell = Roo.get(t).findParent('th', false, true);
6042 Roo.log("failed to find th in thead?");
6043 Roo.log(e.getTarget());
6048 var cellIndex = cell.dom.cellIndex;
6050 var ename = name == 'touchstart' ? 'click' : name;
6051 this.fireEvent("header" + ename, this, cellIndex, e);
6056 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6057 cell = Roo.get(t).findParent('td', false, true);
6059 Roo.log("failed to find th in tbody?");
6060 Roo.log(e.getTarget());
6065 var row = cell.findParent('tr', false, true);
6066 var cellIndex = cell.dom.cellIndex;
6067 var rowIndex = row.dom.rowIndex - 1;
6071 this.fireEvent("row" + name, this, rowIndex, e);
6075 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6081 onMouseover : function(e, el)
6083 var cell = Roo.get(el);
6089 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6090 cell = cell.findParent('td', false, true);
6093 var row = cell.findParent('tr', false, true);
6094 var cellIndex = cell.dom.cellIndex;
6095 var rowIndex = row.dom.rowIndex - 1; // start from 0
6097 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6101 onMouseout : function(e, el)
6103 var cell = Roo.get(el);
6109 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6110 cell = cell.findParent('td', false, true);
6113 var row = cell.findParent('tr', false, true);
6114 var cellIndex = cell.dom.cellIndex;
6115 var rowIndex = row.dom.rowIndex - 1; // start from 0
6117 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6121 onClick : function(e, el)
6123 var cell = Roo.get(el);
6125 if(!cell || (!this.cellSelection && !this.rowSelection)){
6129 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6130 cell = cell.findParent('td', false, true);
6133 if(!cell || typeof(cell) == 'undefined'){
6137 var row = cell.findParent('tr', false, true);
6139 if(!row || typeof(row) == 'undefined'){
6143 var cellIndex = cell.dom.cellIndex;
6144 var rowIndex = this.getRowIndex(row);
6146 // why??? - should these not be based on SelectionModel?
6147 if(this.cellSelection){
6148 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6151 if(this.rowSelection){
6152 this.fireEvent('rowclick', this, row, rowIndex, e);
6158 onDblClick : function(e,el)
6160 var cell = Roo.get(el);
6162 if(!cell || (!this.CellSelection && !this.RowSelection)){
6166 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6167 cell = cell.findParent('td', false, true);
6170 if(!cell || typeof(cell) == 'undefined'){
6174 var row = cell.findParent('tr', false, true);
6176 if(!row || typeof(row) == 'undefined'){
6180 var cellIndex = cell.dom.cellIndex;
6181 var rowIndex = this.getRowIndex(row);
6183 if(this.CellSelection){
6184 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6187 if(this.RowSelection){
6188 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6192 sort : function(e,el)
6194 var col = Roo.get(el);
6196 if(!col.hasClass('sortable')){
6200 var sort = col.attr('sort');
6203 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6207 this.store.sortInfo = {field : sort, direction : dir};
6210 Roo.log("calling footer first");
6211 this.footer.onClick('first');
6214 this.store.load({ params : { start : 0 } });
6218 renderHeader : function()
6226 this.totalWidth = 0;
6228 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6230 var config = cm.config[i];
6235 html: cm.getColumnHeader(i)
6240 if(typeof(config.sortable) != 'undefined' && config.sortable){
6242 c.html = '<i class="glyphicon"></i>' + c.html;
6245 if(typeof(config.lgHeader) != 'undefined'){
6246 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6249 if(typeof(config.mdHeader) != 'undefined'){
6250 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6253 if(typeof(config.smHeader) != 'undefined'){
6254 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6257 if(typeof(config.xsHeader) != 'undefined'){
6258 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6265 if(typeof(config.tooltip) != 'undefined'){
6266 c.tooltip = config.tooltip;
6269 if(typeof(config.colspan) != 'undefined'){
6270 c.colspan = config.colspan;
6273 if(typeof(config.hidden) != 'undefined' && config.hidden){
6274 c.style += ' display:none;';
6277 if(typeof(config.dataIndex) != 'undefined'){
6278 c.sort = config.dataIndex;
6283 if(typeof(config.align) != 'undefined' && config.align.length){
6284 c.style += ' text-align:' + config.align + ';';
6287 if(typeof(config.width) != 'undefined'){
6288 c.style += ' width:' + config.width + 'px;';
6289 this.totalWidth += config.width;
6291 this.totalWidth += 100; // assume minimum of 100 per column?
6294 if(typeof(config.cls) != 'undefined'){
6295 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6298 ['xs','sm','md','lg'].map(function(size){
6300 if(typeof(config[size]) == 'undefined'){
6304 if (!config[size]) { // 0 = hidden
6305 c.cls += ' hidden-' + size;
6309 c.cls += ' col-' + size + '-' + config[size];
6319 renderBody : function()
6329 colspan : this.cm.getColumnCount()
6339 renderFooter : function()
6349 colspan : this.cm.getColumnCount()
6363 // Roo.log('ds onload');
6368 var ds = this.store;
6370 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6371 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6372 if (_this.store.sortInfo) {
6374 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6375 e.select('i', true).addClass(['glyphicon-arrow-up']);
6378 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6379 e.select('i', true).addClass(['glyphicon-arrow-down']);
6384 var tbody = this.mainBody;
6386 if(ds.getCount() > 0){
6387 ds.data.each(function(d,rowIndex){
6388 var row = this.renderRow(cm, ds, rowIndex);
6390 tbody.createChild(row);
6394 if(row.cellObjects.length){
6395 Roo.each(row.cellObjects, function(r){
6396 _this.renderCellObject(r);
6403 Roo.each(this.el.select('tbody td', true).elements, function(e){
6404 e.on('mouseover', _this.onMouseover, _this);
6407 Roo.each(this.el.select('tbody td', true).elements, function(e){
6408 e.on('mouseout', _this.onMouseout, _this);
6410 this.fireEvent('rowsrendered', this);
6411 //if(this.loadMask){
6412 // this.maskEl.hide();
6419 onUpdate : function(ds,record)
6421 this.refreshRow(record);
6424 onRemove : function(ds, record, index, isUpdate){
6425 if(isUpdate !== true){
6426 this.fireEvent("beforerowremoved", this, index, record);
6428 var bt = this.mainBody.dom;
6430 var rows = this.el.select('tbody > tr', true).elements;
6432 if(typeof(rows[index]) != 'undefined'){
6433 bt.removeChild(rows[index].dom);
6436 // if(bt.rows[index]){
6437 // bt.removeChild(bt.rows[index]);
6440 if(isUpdate !== true){
6441 //this.stripeRows(index);
6442 //this.syncRowHeights(index, index);
6444 this.fireEvent("rowremoved", this, index, record);
6448 onAdd : function(ds, records, rowIndex)
6450 //Roo.log('on Add called');
6451 // - note this does not handle multiple adding very well..
6452 var bt = this.mainBody.dom;
6453 for (var i =0 ; i < records.length;i++) {
6454 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6455 //Roo.log(records[i]);
6456 //Roo.log(this.store.getAt(rowIndex+i));
6457 this.insertRow(this.store, rowIndex + i, false);
6464 refreshRow : function(record){
6465 var ds = this.store, index;
6466 if(typeof record == 'number'){
6468 record = ds.getAt(index);
6470 index = ds.indexOf(record);
6472 this.insertRow(ds, index, true);
6473 this.onRemove(ds, record, index+1, true);
6474 //this.syncRowHeights(index, index);
6476 this.fireEvent("rowupdated", this, index, record);
6479 insertRow : function(dm, rowIndex, isUpdate){
6482 this.fireEvent("beforerowsinserted", this, rowIndex);
6484 //var s = this.getScrollState();
6485 var row = this.renderRow(this.cm, this.store, rowIndex);
6486 // insert before rowIndex..
6487 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6491 if(row.cellObjects.length){
6492 Roo.each(row.cellObjects, function(r){
6493 _this.renderCellObject(r);
6498 this.fireEvent("rowsinserted", this, rowIndex);
6499 //this.syncRowHeights(firstRow, lastRow);
6500 //this.stripeRows(firstRow);
6507 getRowDom : function(rowIndex)
6509 var rows = this.el.select('tbody > tr', true).elements;
6511 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6514 // returns the object tree for a tr..
6517 renderRow : function(cm, ds, rowIndex)
6520 var d = ds.getAt(rowIndex);
6527 var cellObjects = [];
6529 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6530 var config = cm.config[i];
6532 var renderer = cm.getRenderer(i);
6536 if(typeof(renderer) !== 'undefined'){
6537 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6539 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6540 // and are rendered into the cells after the row is rendered - using the id for the element.
6542 if(typeof(value) === 'object'){
6552 rowIndex : rowIndex,
6557 this.fireEvent('rowclass', this, rowcfg);
6561 cls : rowcfg.rowClass,
6563 html: (typeof(value) === 'object') ? '' : value
6570 if(typeof(config.colspan) != 'undefined'){
6571 td.colspan = config.colspan;
6574 if(typeof(config.hidden) != 'undefined' && config.hidden){
6575 td.style += ' display:none;';
6578 if(typeof(config.align) != 'undefined' && config.align.length){
6579 td.style += ' text-align:' + config.align + ';';
6582 if(typeof(config.width) != 'undefined'){
6583 td.style += ' width:' + config.width + 'px;';
6586 if(typeof(config.cursor) != 'undefined'){
6587 td.style += ' cursor:' + config.cursor + ';';
6590 if(typeof(config.cls) != 'undefined'){
6591 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6594 ['xs','sm','md','lg'].map(function(size){
6596 if(typeof(config[size]) == 'undefined'){
6600 if (!config[size]) { // 0 = hidden
6601 td.cls += ' hidden-' + size;
6605 td.cls += ' col-' + size + '-' + config[size];
6613 row.cellObjects = cellObjects;
6621 onBeforeLoad : function()
6623 //Roo.log('ds onBeforeLoad');
6627 //if(this.loadMask){
6628 // this.maskEl.show();
6636 this.el.select('tbody', true).first().dom.innerHTML = '';
6639 * Show or hide a row.
6640 * @param {Number} rowIndex to show or hide
6641 * @param {Boolean} state hide
6643 setRowVisibility : function(rowIndex, state)
6645 var bt = this.mainBody.dom;
6647 var rows = this.el.select('tbody > tr', true).elements;
6649 if(typeof(rows[rowIndex]) == 'undefined'){
6652 rows[rowIndex].dom.style.display = state ? '' : 'none';
6656 getSelectionModel : function(){
6658 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6660 return this.selModel;
6663 * Render the Roo.bootstrap object from renderder
6665 renderCellObject : function(r)
6669 var t = r.cfg.render(r.container);
6672 Roo.each(r.cfg.cn, function(c){
6674 container: t.getChildContainer(),
6677 _this.renderCellObject(child);
6682 getRowIndex : function(row)
6686 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6697 * Returns the grid's underlying element = used by panel.Grid
6698 * @return {Element} The element
6700 getGridEl : function(){
6704 * Forces a resize - used by panel.Grid
6705 * @return {Element} The element
6707 autoSize : function()
6709 //var ctr = Roo.get(this.container.dom.parentElement);
6710 var ctr = Roo.get(this.el.dom);
6712 var thd = this.getGridEl().select('thead',true).first();
6713 var tbd = this.getGridEl().select('tbody', true).first();
6716 var cw = ctr.getWidth();
6720 tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6721 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6724 cw = Math.max(cw, this.totalWidth);
6725 this.getGridEl().select('tr',true).setWidth(cw);
6726 // resize 'expandable coloumn?
6728 return; // we doe not have a view in this design..
6731 onBodyScroll: function()
6734 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6735 this.mainHead.setStyle({
6736 'position' : 'relative',
6737 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6754 * @class Roo.bootstrap.TableCell
6755 * @extends Roo.bootstrap.Component
6756 * Bootstrap TableCell class
6757 * @cfg {String} html cell contain text
6758 * @cfg {String} cls cell class
6759 * @cfg {String} tag cell tag (td|th) default td
6760 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6761 * @cfg {String} align Aligns the content in a cell
6762 * @cfg {String} axis Categorizes cells
6763 * @cfg {String} bgcolor Specifies the background color of a cell
6764 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6765 * @cfg {Number} colspan Specifies the number of columns a cell should span
6766 * @cfg {String} headers Specifies one or more header cells a cell is related to
6767 * @cfg {Number} height Sets the height of a cell
6768 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6769 * @cfg {Number} rowspan Sets the number of rows a cell should span
6770 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6771 * @cfg {String} valign Vertical aligns the content in a cell
6772 * @cfg {Number} width Specifies the width of a cell
6775 * Create a new TableCell
6776 * @param {Object} config The config object
6779 Roo.bootstrap.TableCell = function(config){
6780 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6783 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6803 getAutoCreate : function(){
6804 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6824 cfg.align=this.align
6830 cfg.bgcolor=this.bgcolor
6833 cfg.charoff=this.charoff
6836 cfg.colspan=this.colspan
6839 cfg.headers=this.headers
6842 cfg.height=this.height
6845 cfg.nowrap=this.nowrap
6848 cfg.rowspan=this.rowspan
6851 cfg.scope=this.scope
6854 cfg.valign=this.valign
6857 cfg.width=this.width
6876 * @class Roo.bootstrap.TableRow
6877 * @extends Roo.bootstrap.Component
6878 * Bootstrap TableRow class
6879 * @cfg {String} cls row class
6880 * @cfg {String} align Aligns the content in a table row
6881 * @cfg {String} bgcolor Specifies a background color for a table row
6882 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6883 * @cfg {String} valign Vertical aligns the content in a table row
6886 * Create a new TableRow
6887 * @param {Object} config The config object
6890 Roo.bootstrap.TableRow = function(config){
6891 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6894 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6902 getAutoCreate : function(){
6903 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6913 cfg.align = this.align;
6916 cfg.bgcolor = this.bgcolor;
6919 cfg.charoff = this.charoff;
6922 cfg.valign = this.valign;
6940 * @class Roo.bootstrap.TableBody
6941 * @extends Roo.bootstrap.Component
6942 * Bootstrap TableBody class
6943 * @cfg {String} cls element class
6944 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6945 * @cfg {String} align Aligns the content inside the element
6946 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6947 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6950 * Create a new TableBody
6951 * @param {Object} config The config object
6954 Roo.bootstrap.TableBody = function(config){
6955 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6958 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6966 getAutoCreate : function(){
6967 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6981 cfg.align = this.align;
6984 cfg.charoff = this.charoff;
6987 cfg.valign = this.valign;
6994 // initEvents : function()
7001 // this.store = Roo.factory(this.store, Roo.data);
7002 // this.store.on('load', this.onLoad, this);
7004 // this.store.load();
7008 // onLoad: function ()
7010 // this.fireEvent('load', this);
7020 * Ext JS Library 1.1.1
7021 * Copyright(c) 2006-2007, Ext JS, LLC.
7023 * Originally Released Under LGPL - original licence link has changed is not relivant.
7026 * <script type="text/javascript">
7029 // as we use this in bootstrap.
7030 Roo.namespace('Roo.form');
7032 * @class Roo.form.Action
7033 * Internal Class used to handle form actions
7035 * @param {Roo.form.BasicForm} el The form element or its id
7036 * @param {Object} config Configuration options
7041 // define the action interface
7042 Roo.form.Action = function(form, options){
7044 this.options = options || {};
7047 * Client Validation Failed
7050 Roo.form.Action.CLIENT_INVALID = 'client';
7052 * Server Validation Failed
7055 Roo.form.Action.SERVER_INVALID = 'server';
7057 * Connect to Server Failed
7060 Roo.form.Action.CONNECT_FAILURE = 'connect';
7062 * Reading Data from Server Failed
7065 Roo.form.Action.LOAD_FAILURE = 'load';
7067 Roo.form.Action.prototype = {
7069 failureType : undefined,
7070 response : undefined,
7074 run : function(options){
7079 success : function(response){
7084 handleResponse : function(response){
7088 // default connection failure
7089 failure : function(response){
7091 this.response = response;
7092 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7093 this.form.afterAction(this, false);
7096 processResponse : function(response){
7097 this.response = response;
7098 if(!response.responseText){
7101 this.result = this.handleResponse(response);
7105 // utility functions used internally
7106 getUrl : function(appendParams){
7107 var url = this.options.url || this.form.url || this.form.el.dom.action;
7109 var p = this.getParams();
7111 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7117 getMethod : function(){
7118 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7121 getParams : function(){
7122 var bp = this.form.baseParams;
7123 var p = this.options.params;
7125 if(typeof p == "object"){
7126 p = Roo.urlEncode(Roo.applyIf(p, bp));
7127 }else if(typeof p == 'string' && bp){
7128 p += '&' + Roo.urlEncode(bp);
7131 p = Roo.urlEncode(bp);
7136 createCallback : function(){
7138 success: this.success,
7139 failure: this.failure,
7141 timeout: (this.form.timeout*1000),
7142 upload: this.form.fileUpload ? this.success : undefined
7147 Roo.form.Action.Submit = function(form, options){
7148 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7151 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7154 haveProgress : false,
7155 uploadComplete : false,
7157 // uploadProgress indicator.
7158 uploadProgress : function()
7160 if (!this.form.progressUrl) {
7164 if (!this.haveProgress) {
7165 Roo.MessageBox.progress("Uploading", "Uploading");
7167 if (this.uploadComplete) {
7168 Roo.MessageBox.hide();
7172 this.haveProgress = true;
7174 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7176 var c = new Roo.data.Connection();
7178 url : this.form.progressUrl,
7183 success : function(req){
7184 //console.log(data);
7188 rdata = Roo.decode(req.responseText)
7190 Roo.log("Invalid data from server..");
7194 if (!rdata || !rdata.success) {
7196 Roo.MessageBox.alert(Roo.encode(rdata));
7199 var data = rdata.data;
7201 if (this.uploadComplete) {
7202 Roo.MessageBox.hide();
7207 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7208 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7211 this.uploadProgress.defer(2000,this);
7214 failure: function(data) {
7215 Roo.log('progress url failed ');
7226 // run get Values on the form, so it syncs any secondary forms.
7227 this.form.getValues();
7229 var o = this.options;
7230 var method = this.getMethod();
7231 var isPost = method == 'POST';
7232 if(o.clientValidation === false || this.form.isValid()){
7234 if (this.form.progressUrl) {
7235 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7236 (new Date() * 1) + '' + Math.random());
7241 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7242 form:this.form.el.dom,
7243 url:this.getUrl(!isPost),
7245 params:isPost ? this.getParams() : null,
7246 isUpload: this.form.fileUpload
7249 this.uploadProgress();
7251 }else if (o.clientValidation !== false){ // client validation failed
7252 this.failureType = Roo.form.Action.CLIENT_INVALID;
7253 this.form.afterAction(this, false);
7257 success : function(response)
7259 this.uploadComplete= true;
7260 if (this.haveProgress) {
7261 Roo.MessageBox.hide();
7265 var result = this.processResponse(response);
7266 if(result === true || result.success){
7267 this.form.afterAction(this, true);
7271 this.form.markInvalid(result.errors);
7272 this.failureType = Roo.form.Action.SERVER_INVALID;
7274 this.form.afterAction(this, false);
7276 failure : function(response)
7278 this.uploadComplete= true;
7279 if (this.haveProgress) {
7280 Roo.MessageBox.hide();
7283 this.response = response;
7284 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7285 this.form.afterAction(this, false);
7288 handleResponse : function(response){
7289 if(this.form.errorReader){
7290 var rs = this.form.errorReader.read(response);
7293 for(var i = 0, len = rs.records.length; i < len; i++) {
7294 var r = rs.records[i];
7298 if(errors.length < 1){
7302 success : rs.success,
7308 ret = Roo.decode(response.responseText);
7312 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7322 Roo.form.Action.Load = function(form, options){
7323 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7324 this.reader = this.form.reader;
7327 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7332 Roo.Ajax.request(Roo.apply(
7333 this.createCallback(), {
7334 method:this.getMethod(),
7335 url:this.getUrl(false),
7336 params:this.getParams()
7340 success : function(response){
7342 var result = this.processResponse(response);
7343 if(result === true || !result.success || !result.data){
7344 this.failureType = Roo.form.Action.LOAD_FAILURE;
7345 this.form.afterAction(this, false);
7348 this.form.clearInvalid();
7349 this.form.setValues(result.data);
7350 this.form.afterAction(this, true);
7353 handleResponse : function(response){
7354 if(this.form.reader){
7355 var rs = this.form.reader.read(response);
7356 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7358 success : rs.success,
7362 return Roo.decode(response.responseText);
7366 Roo.form.Action.ACTION_TYPES = {
7367 'load' : Roo.form.Action.Load,
7368 'submit' : Roo.form.Action.Submit
7377 * @class Roo.bootstrap.Form
7378 * @extends Roo.bootstrap.Component
7379 * Bootstrap Form class
7380 * @cfg {String} method GET | POST (default POST)
7381 * @cfg {String} labelAlign top | left (default top)
7382 * @cfg {String} align left | right - for navbars
7383 * @cfg {Boolean} loadMask load mask when submit (default true)
7388 * @param {Object} config The config object
7392 Roo.bootstrap.Form = function(config){
7393 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7396 * @event clientvalidation
7397 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7398 * @param {Form} this
7399 * @param {Boolean} valid true if the form has passed client-side validation
7401 clientvalidation: true,
7403 * @event beforeaction
7404 * Fires before any action is performed. Return false to cancel the action.
7405 * @param {Form} this
7406 * @param {Action} action The action to be performed
7410 * @event actionfailed
7411 * Fires when an action fails.
7412 * @param {Form} this
7413 * @param {Action} action The action that failed
7415 actionfailed : true,
7417 * @event actioncomplete
7418 * Fires when an action is completed.
7419 * @param {Form} this
7420 * @param {Action} action The action that completed
7422 actioncomplete : true
7427 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7430 * @cfg {String} method
7431 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7436 * The URL to use for form actions if one isn't supplied in the action options.
7439 * @cfg {Boolean} fileUpload
7440 * Set to true if this form is a file upload.
7444 * @cfg {Object} baseParams
7445 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7449 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7453 * @cfg {Sting} align (left|right) for navbar forms
7458 activeAction : null,
7461 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7462 * element by passing it or its id or mask the form itself by passing in true.
7465 waitMsgTarget : false,
7469 getAutoCreate : function(){
7473 method : this.method || 'POST',
7474 id : this.id || Roo.id(),
7477 if (this.parent().xtype.match(/^Nav/)) {
7478 cfg.cls = 'navbar-form navbar-' + this.align;
7482 if (this.labelAlign == 'left' ) {
7483 cfg.cls += ' form-horizontal';
7489 initEvents : function()
7491 this.el.on('submit', this.onSubmit, this);
7492 // this was added as random key presses on the form where triggering form submit.
7493 this.el.on('keypress', function(e) {
7494 if (e.getCharCode() != 13) {
7497 // we might need to allow it for textareas.. and some other items.
7498 // check e.getTarget().
7500 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7504 Roo.log("keypress blocked");
7512 onSubmit : function(e){
7517 * Returns true if client-side validation on the form is successful.
7520 isValid : function(){
7521 var items = this.getItems();
7523 items.each(function(f){
7532 * Returns true if any fields in this form have changed since their original load.
7535 isDirty : function(){
7537 var items = this.getItems();
7538 items.each(function(f){
7548 * Performs a predefined action (submit or load) or custom actions you define on this form.
7549 * @param {String} actionName The name of the action type
7550 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7551 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7552 * accept other config options):
7554 Property Type Description
7555 ---------------- --------------- ----------------------------------------------------------------------------------
7556 url String The url for the action (defaults to the form's url)
7557 method String The form method to use (defaults to the form's method, or POST if not defined)
7558 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7559 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7560 validate the form on the client (defaults to false)
7562 * @return {BasicForm} this
7564 doAction : function(action, options){
7565 if(typeof action == 'string'){
7566 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7568 if(this.fireEvent('beforeaction', this, action) !== false){
7569 this.beforeAction(action);
7570 action.run.defer(100, action);
7576 beforeAction : function(action){
7577 var o = action.options;
7580 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7582 // not really supported yet.. ??
7584 //if(this.waitMsgTarget === true){
7585 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7586 //}else if(this.waitMsgTarget){
7587 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7588 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7590 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7596 afterAction : function(action, success){
7597 this.activeAction = null;
7598 var o = action.options;
7600 //if(this.waitMsgTarget === true){
7602 //}else if(this.waitMsgTarget){
7603 // this.waitMsgTarget.unmask();
7605 // Roo.MessageBox.updateProgress(1);
7606 // Roo.MessageBox.hide();
7613 Roo.callback(o.success, o.scope, [this, action]);
7614 this.fireEvent('actioncomplete', this, action);
7618 // failure condition..
7619 // we have a scenario where updates need confirming.
7620 // eg. if a locking scenario exists..
7621 // we look for { errors : { needs_confirm : true }} in the response.
7623 (typeof(action.result) != 'undefined') &&
7624 (typeof(action.result.errors) != 'undefined') &&
7625 (typeof(action.result.errors.needs_confirm) != 'undefined')
7628 Roo.log("not supported yet");
7631 Roo.MessageBox.confirm(
7632 "Change requires confirmation",
7633 action.result.errorMsg,
7638 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7648 Roo.callback(o.failure, o.scope, [this, action]);
7649 // show an error message if no failed handler is set..
7650 if (!this.hasListener('actionfailed')) {
7651 Roo.log("need to add dialog support");
7653 Roo.MessageBox.alert("Error",
7654 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7655 action.result.errorMsg :
7656 "Saving Failed, please check your entries or try again"
7661 this.fireEvent('actionfailed', this, action);
7666 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7667 * @param {String} id The value to search for
7670 findField : function(id){
7671 var items = this.getItems();
7672 var field = items.get(id);
7674 items.each(function(f){
7675 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7682 return field || null;
7685 * Mark fields in this form invalid in bulk.
7686 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7687 * @return {BasicForm} this
7689 markInvalid : function(errors){
7690 if(errors instanceof Array){
7691 for(var i = 0, len = errors.length; i < len; i++){
7692 var fieldError = errors[i];
7693 var f = this.findField(fieldError.id);
7695 f.markInvalid(fieldError.msg);
7701 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7702 field.markInvalid(errors[id]);
7706 //Roo.each(this.childForms || [], function (f) {
7707 // f.markInvalid(errors);
7714 * Set values for fields in this form in bulk.
7715 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7716 * @return {BasicForm} this
7718 setValues : function(values){
7719 if(values instanceof Array){ // array of objects
7720 for(var i = 0, len = values.length; i < len; i++){
7722 var f = this.findField(v.id);
7724 f.setValue(v.value);
7725 if(this.trackResetOnLoad){
7726 f.originalValue = f.getValue();
7730 }else{ // object hash
7733 if(typeof values[id] != 'function' && (field = this.findField(id))){
7735 if (field.setFromData &&
7737 field.displayField &&
7738 // combos' with local stores can
7739 // be queried via setValue()
7740 // to set their value..
7741 (field.store && !field.store.isLocal)
7745 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7746 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7747 field.setFromData(sd);
7750 field.setValue(values[id]);
7754 if(this.trackResetOnLoad){
7755 field.originalValue = field.getValue();
7761 //Roo.each(this.childForms || [], function (f) {
7762 // f.setValues(values);
7769 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7770 * they are returned as an array.
7771 * @param {Boolean} asString
7774 getValues : function(asString){
7775 //if (this.childForms) {
7776 // copy values from the child forms
7777 // Roo.each(this.childForms, function (f) {
7778 // this.setValues(f.getValues());
7784 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7785 if(asString === true){
7788 return Roo.urlDecode(fs);
7792 * Returns the fields in this form as an object with key/value pairs.
7793 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7796 getFieldValues : function(with_hidden)
7798 var items = this.getItems();
7800 items.each(function(f){
7804 var v = f.getValue();
7805 if (f.inputType =='radio') {
7806 if (typeof(ret[f.getName()]) == 'undefined') {
7807 ret[f.getName()] = ''; // empty..
7810 if (!f.el.dom.checked) {
7818 // not sure if this supported any more..
7819 if ((typeof(v) == 'object') && f.getRawValue) {
7820 v = f.getRawValue() ; // dates..
7822 // combo boxes where name != hiddenName...
7823 if (f.name != f.getName()) {
7824 ret[f.name] = f.getRawValue();
7826 ret[f.getName()] = v;
7833 * Clears all invalid messages in this form.
7834 * @return {BasicForm} this
7836 clearInvalid : function(){
7837 var items = this.getItems();
7839 items.each(function(f){
7850 * @return {BasicForm} this
7853 var items = this.getItems();
7854 items.each(function(f){
7858 Roo.each(this.childForms || [], function (f) {
7865 getItems : function()
7867 var r=new Roo.util.MixedCollection(false, function(o){
7868 return o.id || (o.id = Roo.id());
7870 var iter = function(el) {
7877 Roo.each(el.items,function(e) {
7897 * Ext JS Library 1.1.1
7898 * Copyright(c) 2006-2007, Ext JS, LLC.
7900 * Originally Released Under LGPL - original licence link has changed is not relivant.
7903 * <script type="text/javascript">
7906 * @class Roo.form.VTypes
7907 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7910 Roo.form.VTypes = function(){
7911 // closure these in so they are only created once.
7912 var alpha = /^[a-zA-Z_]+$/;
7913 var alphanum = /^[a-zA-Z0-9_]+$/;
7914 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7915 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7917 // All these messages and functions are configurable
7920 * The function used to validate email addresses
7921 * @param {String} value The email address
7923 'email' : function(v){
7924 return email.test(v);
7927 * The error text to display when the email validation function returns false
7930 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7932 * The keystroke filter mask to be applied on email input
7935 'emailMask' : /[a-z0-9_\.\-@]/i,
7938 * The function used to validate URLs
7939 * @param {String} value The URL
7941 'url' : function(v){
7945 * The error text to display when the url validation function returns false
7948 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7951 * The function used to validate alpha values
7952 * @param {String} value The value
7954 'alpha' : function(v){
7955 return alpha.test(v);
7958 * The error text to display when the alpha validation function returns false
7961 'alphaText' : 'This field should only contain letters and _',
7963 * The keystroke filter mask to be applied on alpha input
7966 'alphaMask' : /[a-z_]/i,
7969 * The function used to validate alphanumeric values
7970 * @param {String} value The value
7972 'alphanum' : function(v){
7973 return alphanum.test(v);
7976 * The error text to display when the alphanumeric validation function returns false
7979 'alphanumText' : 'This field should only contain letters, numbers and _',
7981 * The keystroke filter mask to be applied on alphanumeric input
7984 'alphanumMask' : /[a-z0-9_]/i
7994 * @class Roo.bootstrap.Input
7995 * @extends Roo.bootstrap.Component
7996 * Bootstrap Input class
7997 * @cfg {Boolean} disabled is it disabled
7998 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7999 * @cfg {String} name name of the input
8000 * @cfg {string} fieldLabel - the label associated
8001 * @cfg {string} placeholder - placeholder to put in text.
8002 * @cfg {string} before - input group add on before
8003 * @cfg {string} after - input group add on after
8004 * @cfg {string} size - (lg|sm) or leave empty..
8005 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8006 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8007 * @cfg {Number} md colspan out of 12 for computer-sized screens
8008 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8009 * @cfg {string} value default value of the input
8010 * @cfg {Number} labelWidth set the width of label (0-12)
8011 * @cfg {String} labelAlign (top|left)
8012 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8013 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8014 * @cfg {String} indicatorpos (left|right) default left
8016 * @cfg {String} align (left|center|right) Default left
8017 * @cfg {Boolean} forceFeedback (true|false) Default false
8023 * Create a new Input
8024 * @param {Object} config The config object
8027 Roo.bootstrap.Input = function(config){
8028 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8033 * Fires when this field receives input focus.
8034 * @param {Roo.form.Field} this
8039 * Fires when this field loses input focus.
8040 * @param {Roo.form.Field} this
8045 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8046 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8047 * @param {Roo.form.Field} this
8048 * @param {Roo.EventObject} e The event object
8053 * Fires just before the field blurs if the field value has changed.
8054 * @param {Roo.form.Field} this
8055 * @param {Mixed} newValue The new value
8056 * @param {Mixed} oldValue The original value
8061 * Fires after the field has been marked as invalid.
8062 * @param {Roo.form.Field} this
8063 * @param {String} msg The validation message
8068 * Fires after the field has been validated with no errors.
8069 * @param {Roo.form.Field} this
8074 * Fires after the key up
8075 * @param {Roo.form.Field} this
8076 * @param {Roo.EventObject} e The event Object
8082 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8084 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8085 automatic validation (defaults to "keyup").
8087 validationEvent : "keyup",
8089 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8091 validateOnBlur : true,
8093 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8095 validationDelay : 250,
8097 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8099 focusClass : "x-form-focus", // not needed???
8103 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8105 invalidClass : "has-warning",
8108 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8110 validClass : "has-success",
8113 * @cfg {Boolean} hasFeedback (true|false) default true
8118 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8120 invalidFeedbackClass : "glyphicon-warning-sign",
8123 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8125 validFeedbackClass : "glyphicon-ok",
8128 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8130 selectOnFocus : false,
8133 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8137 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8142 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8144 disableKeyFilter : false,
8147 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8151 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8155 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8157 blankText : "This field is required",
8160 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8164 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8166 maxLength : Number.MAX_VALUE,
8168 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8170 minLengthText : "The minimum length for this field is {0}",
8172 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8174 maxLengthText : "The maximum length for this field is {0}",
8178 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8179 * If available, this function will be called only after the basic validators all return true, and will be passed the
8180 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8184 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8185 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8186 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8190 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8194 autocomplete: false,
8213 formatedValue : false,
8214 forceFeedback : false,
8216 indicatorpos : 'left',
8218 parentLabelAlign : function()
8221 while (parent.parent()) {
8222 parent = parent.parent();
8223 if (typeof(parent.labelAlign) !='undefined') {
8224 return parent.labelAlign;
8231 getAutoCreate : function()
8233 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8239 if(this.inputType != 'hidden'){
8240 cfg.cls = 'form-group' //input-group
8246 type : this.inputType,
8248 cls : 'form-control',
8249 placeholder : this.placeholder || '',
8250 autocomplete : this.autocomplete || 'new-password'
8254 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8257 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8258 input.maxLength = this.maxLength;
8261 if (this.disabled) {
8262 input.disabled=true;
8265 if (this.readOnly) {
8266 input.readonly=true;
8270 input.name = this.name;
8274 input.cls += ' input-' + this.size;
8278 ['xs','sm','md','lg'].map(function(size){
8279 if (settings[size]) {
8280 cfg.cls += ' col-' + size + '-' + settings[size];
8284 var inputblock = input;
8288 cls: 'glyphicon form-control-feedback'
8291 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8294 cls : 'has-feedback',
8302 if (this.before || this.after) {
8305 cls : 'input-group',
8309 if (this.before && typeof(this.before) == 'string') {
8311 inputblock.cn.push({
8313 cls : 'roo-input-before input-group-addon',
8317 if (this.before && typeof(this.before) == 'object') {
8318 this.before = Roo.factory(this.before);
8320 inputblock.cn.push({
8322 cls : 'roo-input-before input-group-' +
8323 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8327 inputblock.cn.push(input);
8329 if (this.after && typeof(this.after) == 'string') {
8330 inputblock.cn.push({
8332 cls : 'roo-input-after input-group-addon',
8336 if (this.after && typeof(this.after) == 'object') {
8337 this.after = Roo.factory(this.after);
8339 inputblock.cn.push({
8341 cls : 'roo-input-after input-group-' +
8342 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8346 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8347 inputblock.cls += ' has-feedback';
8348 inputblock.cn.push(feedback);
8352 if (align ==='left' && this.fieldLabel.length) {
8357 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8358 tooltip : 'This field is required'
8363 cls : 'control-label col-sm-' + this.labelWidth,
8364 html : this.fieldLabel
8368 cls : "col-sm-" + (12 - this.labelWidth),
8376 if(this.indicatorpos == 'right'){
8381 cls : 'control-label col-sm-' + this.labelWidth,
8382 html : this.fieldLabel
8387 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8388 tooltip : 'This field is required'
8391 cls : "col-sm-" + (12 - this.labelWidth),
8400 } else if ( this.fieldLabel.length) {
8405 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8406 tooltip : 'This field is required'
8410 //cls : 'input-group-addon',
8411 html : this.fieldLabel
8419 if(this.indicatorpos == 'right'){
8424 //cls : 'input-group-addon',
8425 html : this.fieldLabel
8430 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8431 tooltip : 'This field is required'
8451 if (this.parentType === 'Navbar' && this.parent().bar) {
8452 cfg.cls += ' navbar-form';
8455 if (this.parentType === 'NavGroup') {
8456 cfg.cls += ' navbar-form';
8464 * return the real input element.
8466 inputEl: function ()
8468 return this.el.select('input.form-control',true).first();
8471 tooltipEl : function()
8473 return this.inputEl();
8476 indicatorEl : function()
8478 var indicator = this.el.select('i.roo-required-indicator',true).first();
8488 setDisabled : function(v)
8490 var i = this.inputEl().dom;
8492 i.removeAttribute('disabled');
8496 i.setAttribute('disabled','true');
8498 initEvents : function()
8501 this.inputEl().on("keydown" , this.fireKey, this);
8502 this.inputEl().on("focus", this.onFocus, this);
8503 this.inputEl().on("blur", this.onBlur, this);
8505 this.inputEl().relayEvent('keyup', this);
8507 this.indicator = this.indicatorEl();
8510 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8511 this.indicator.hide();
8514 // reference to original value for reset
8515 this.originalValue = this.getValue();
8516 //Roo.form.TextField.superclass.initEvents.call(this);
8517 if(this.validationEvent == 'keyup'){
8518 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8519 this.inputEl().on('keyup', this.filterValidation, this);
8521 else if(this.validationEvent !== false){
8522 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8525 if(this.selectOnFocus){
8526 this.on("focus", this.preFocus, this);
8529 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8530 this.inputEl().on("keypress", this.filterKeys, this);
8533 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8534 this.el.on("click", this.autoSize, this);
8537 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8538 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8541 if (typeof(this.before) == 'object') {
8542 this.before.render(this.el.select('.roo-input-before',true).first());
8544 if (typeof(this.after) == 'object') {
8545 this.after.render(this.el.select('.roo-input-after',true).first());
8550 filterValidation : function(e){
8551 if(!e.isNavKeyPress()){
8552 this.validationTask.delay(this.validationDelay);
8556 * Validates the field value
8557 * @return {Boolean} True if the value is valid, else false
8559 validate : function(){
8560 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8561 if(this.disabled || this.validateValue(this.getRawValue())){
8572 * Validates a value according to the field's validation rules and marks the field as invalid
8573 * if the validation fails
8574 * @param {Mixed} value The value to validate
8575 * @return {Boolean} True if the value is valid, else false
8577 validateValue : function(value){
8578 if(value.length < 1) { // if it's blank
8579 if(this.allowBlank){
8585 if(value.length < this.minLength){
8588 if(value.length > this.maxLength){
8592 var vt = Roo.form.VTypes;
8593 if(!vt[this.vtype](value, this)){
8597 if(typeof this.validator == "function"){
8598 var msg = this.validator(value);
8604 if(this.regex && !this.regex.test(value)){
8614 fireKey : function(e){
8615 //Roo.log('field ' + e.getKey());
8616 if(e.isNavKeyPress()){
8617 this.fireEvent("specialkey", this, e);
8620 focus : function (selectText){
8622 this.inputEl().focus();
8623 if(selectText === true){
8624 this.inputEl().dom.select();
8630 onFocus : function(){
8631 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8632 // this.el.addClass(this.focusClass);
8635 this.hasFocus = true;
8636 this.startValue = this.getValue();
8637 this.fireEvent("focus", this);
8641 beforeBlur : Roo.emptyFn,
8645 onBlur : function(){
8647 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8648 //this.el.removeClass(this.focusClass);
8650 this.hasFocus = false;
8651 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8654 var v = this.getValue();
8655 if(String(v) !== String(this.startValue)){
8656 this.fireEvent('change', this, v, this.startValue);
8658 this.fireEvent("blur", this);
8662 * Resets the current field value to the originally loaded value and clears any validation messages
8665 this.setValue(this.originalValue);
8669 * Returns the name of the field
8670 * @return {Mixed} name The name field
8672 getName: function(){
8676 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8677 * @return {Mixed} value The field value
8679 getValue : function(){
8681 var v = this.inputEl().getValue();
8686 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8687 * @return {Mixed} value The field value
8689 getRawValue : function(){
8690 var v = this.inputEl().getValue();
8696 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8697 * @param {Mixed} value The value to set
8699 setRawValue : function(v){
8700 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8703 selectText : function(start, end){
8704 var v = this.getRawValue();
8706 start = start === undefined ? 0 : start;
8707 end = end === undefined ? v.length : end;
8708 var d = this.inputEl().dom;
8709 if(d.setSelectionRange){
8710 d.setSelectionRange(start, end);
8711 }else if(d.createTextRange){
8712 var range = d.createTextRange();
8713 range.moveStart("character", start);
8714 range.moveEnd("character", v.length-end);
8721 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8722 * @param {Mixed} value The value to set
8724 setValue : function(v){
8727 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8733 processValue : function(value){
8734 if(this.stripCharsRe){
8735 var newValue = value.replace(this.stripCharsRe, '');
8736 if(newValue !== value){
8737 this.setRawValue(newValue);
8744 preFocus : function(){
8746 if(this.selectOnFocus){
8747 this.inputEl().dom.select();
8750 filterKeys : function(e){
8752 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8755 var c = e.getCharCode(), cc = String.fromCharCode(c);
8756 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8759 if(!this.maskRe.test(cc)){
8764 * Clear any invalid styles/messages for this field
8766 clearInvalid : function(){
8768 if(!this.el || this.preventMark){ // not rendered
8773 this.indicator.hide();
8776 this.el.removeClass(this.invalidClass);
8778 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8780 var feedback = this.el.select('.form-control-feedback', true).first();
8783 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8788 this.fireEvent('valid', this);
8792 * Mark this field as valid
8794 markValid : function()
8796 if(!this.el || this.preventMark){ // not rendered
8800 this.el.removeClass([this.invalidClass, this.validClass]);
8802 var feedback = this.el.select('.form-control-feedback', true).first();
8805 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8808 if(this.disabled || this.allowBlank){
8813 this.indicator.hide();
8816 this.el.addClass(this.validClass);
8818 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8820 var feedback = this.el.select('.form-control-feedback', true).first();
8823 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8824 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8829 this.fireEvent('valid', this);
8833 * Mark this field as invalid
8834 * @param {String} msg The validation message
8836 markInvalid : function(msg)
8838 if(!this.el || this.preventMark){ // not rendered
8842 this.el.removeClass([this.invalidClass, this.validClass]);
8844 var feedback = this.el.select('.form-control-feedback', true).first();
8847 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8850 if(this.disabled || this.allowBlank){
8855 this.indicator.show();
8858 this.el.addClass(this.invalidClass);
8860 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8862 var feedback = this.el.select('.form-control-feedback', true).first();
8865 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8867 if(this.getValue().length || this.forceFeedback){
8868 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8875 this.fireEvent('invalid', this, msg);
8878 SafariOnKeyDown : function(event)
8880 // this is a workaround for a password hang bug on chrome/ webkit.
8882 var isSelectAll = false;
8884 if(this.inputEl().dom.selectionEnd > 0){
8885 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8887 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8888 event.preventDefault();
8893 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8895 event.preventDefault();
8896 // this is very hacky as keydown always get's upper case.
8898 var cc = String.fromCharCode(event.getCharCode());
8899 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8903 adjustWidth : function(tag, w){
8904 tag = tag.toLowerCase();
8905 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8906 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8910 if(tag == 'textarea'){
8913 }else if(Roo.isOpera){
8917 if(tag == 'textarea'){
8936 * @class Roo.bootstrap.TextArea
8937 * @extends Roo.bootstrap.Input
8938 * Bootstrap TextArea class
8939 * @cfg {Number} cols Specifies the visible width of a text area
8940 * @cfg {Number} rows Specifies the visible number of lines in a text area
8941 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8942 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8943 * @cfg {string} html text
8946 * Create a new TextArea
8947 * @param {Object} config The config object
8950 Roo.bootstrap.TextArea = function(config){
8951 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8955 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8965 getAutoCreate : function(){
8967 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8978 value : this.value || '',
8979 html: this.html || '',
8980 cls : 'form-control',
8981 placeholder : this.placeholder || ''
8985 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8986 input.maxLength = this.maxLength;
8990 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8994 input.cols = this.cols;
8997 if (this.readOnly) {
8998 input.readonly = true;
9002 input.name = this.name;
9006 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9010 ['xs','sm','md','lg'].map(function(size){
9011 if (settings[size]) {
9012 cfg.cls += ' col-' + size + '-' + settings[size];
9016 var inputblock = input;
9018 if(this.hasFeedback && !this.allowBlank){
9022 cls: 'glyphicon form-control-feedback'
9026 cls : 'has-feedback',
9035 if (this.before || this.after) {
9038 cls : 'input-group',
9042 inputblock.cn.push({
9044 cls : 'input-group-addon',
9049 inputblock.cn.push(input);
9051 if(this.hasFeedback && !this.allowBlank){
9052 inputblock.cls += ' has-feedback';
9053 inputblock.cn.push(feedback);
9057 inputblock.cn.push({
9059 cls : 'input-group-addon',
9066 if (align ==='left' && this.fieldLabel.length) {
9067 // Roo.log("left and has label");
9073 cls : 'control-label col-sm-' + this.labelWidth,
9074 html : this.fieldLabel
9078 cls : "col-sm-" + (12 - this.labelWidth),
9085 } else if ( this.fieldLabel.length) {
9086 // Roo.log(" label");
9091 //cls : 'input-group-addon',
9092 html : this.fieldLabel
9102 // Roo.log(" no label && no align");
9112 if (this.disabled) {
9113 input.disabled=true;
9120 * return the real textarea element.
9122 inputEl: function ()
9124 return this.el.select('textarea.form-control',true).first();
9128 * Clear any invalid styles/messages for this field
9130 clearInvalid : function()
9133 if(!this.el || this.preventMark){ // not rendered
9137 var label = this.el.select('label', true).first();
9138 var icon = this.el.select('i.fa-star', true).first();
9144 this.el.removeClass(this.invalidClass);
9146 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9148 var feedback = this.el.select('.form-control-feedback', true).first();
9151 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9156 this.fireEvent('valid', this);
9160 * Mark this field as valid
9162 markValid : function()
9164 if(!this.el || this.preventMark){ // not rendered
9168 this.el.removeClass([this.invalidClass, this.validClass]);
9170 var feedback = this.el.select('.form-control-feedback', true).first();
9173 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9176 if(this.disabled || this.allowBlank){
9180 var label = this.el.select('label', true).first();
9181 var icon = this.el.select('i.fa-star', true).first();
9187 this.el.addClass(this.validClass);
9189 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9191 var feedback = this.el.select('.form-control-feedback', true).first();
9194 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9195 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9200 this.fireEvent('valid', this);
9204 * Mark this field as invalid
9205 * @param {String} msg The validation message
9207 markInvalid : function(msg)
9209 if(!this.el || this.preventMark){ // not rendered
9213 this.el.removeClass([this.invalidClass, this.validClass]);
9215 var feedback = this.el.select('.form-control-feedback', true).first();
9218 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9221 if(this.disabled || this.allowBlank){
9225 var label = this.el.select('label', true).first();
9226 var icon = this.el.select('i.fa-star', true).first();
9228 if(!this.getValue().length && label && !icon){
9229 this.el.createChild({
9231 cls : 'text-danger fa fa-lg fa-star',
9232 tooltip : 'This field is required',
9233 style : 'margin-right:5px;'
9237 this.el.addClass(this.invalidClass);
9239 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9241 var feedback = this.el.select('.form-control-feedback', true).first();
9244 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9246 if(this.getValue().length || this.forceFeedback){
9247 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9254 this.fireEvent('invalid', this, msg);
9262 * trigger field - base class for combo..
9267 * @class Roo.bootstrap.TriggerField
9268 * @extends Roo.bootstrap.Input
9269 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9270 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9271 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9272 * for which you can provide a custom implementation. For example:
9274 var trigger = new Roo.bootstrap.TriggerField();
9275 trigger.onTriggerClick = myTriggerFn;
9276 trigger.applyTo('my-field');
9279 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9280 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9281 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9282 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9283 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9286 * Create a new TriggerField.
9287 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9288 * to the base TextField)
9290 Roo.bootstrap.TriggerField = function(config){
9291 this.mimicing = false;
9292 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9295 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9297 * @cfg {String} triggerClass A CSS class to apply to the trigger
9300 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9305 * @cfg {Boolean} removable (true|false) special filter default false
9309 /** @cfg {Boolean} grow @hide */
9310 /** @cfg {Number} growMin @hide */
9311 /** @cfg {Number} growMax @hide */
9317 autoSize: Roo.emptyFn,
9324 actionMode : 'wrap',
9329 getAutoCreate : function(){
9331 var align = this.labelAlign || this.parentLabelAlign();
9336 cls: 'form-group' //input-group
9343 type : this.inputType,
9344 cls : 'form-control',
9345 autocomplete: 'new-password',
9346 placeholder : this.placeholder || ''
9350 input.name = this.name;
9353 input.cls += ' input-' + this.size;
9356 if (this.disabled) {
9357 input.disabled=true;
9360 var inputblock = input;
9362 if(this.hasFeedback && !this.allowBlank){
9366 cls: 'glyphicon form-control-feedback'
9369 if(this.removable && !this.editable && !this.tickable){
9371 cls : 'has-feedback',
9377 cls : 'roo-combo-removable-btn close'
9384 cls : 'has-feedback',
9393 if(this.removable && !this.editable && !this.tickable){
9395 cls : 'roo-removable',
9401 cls : 'roo-combo-removable-btn close'
9408 if (this.before || this.after) {
9411 cls : 'input-group',
9415 inputblock.cn.push({
9417 cls : 'input-group-addon',
9422 inputblock.cn.push(input);
9424 if(this.hasFeedback && !this.allowBlank){
9425 inputblock.cls += ' has-feedback';
9426 inputblock.cn.push(feedback);
9430 inputblock.cn.push({
9432 cls : 'input-group-addon',
9445 cls: 'form-hidden-field'
9459 cls: 'form-hidden-field'
9463 cls: 'roo-select2-choices',
9467 cls: 'roo-select2-search-field',
9480 cls: 'roo-select2-container input-group',
9485 // cls: 'typeahead typeahead-long dropdown-menu',
9486 // style: 'display:none'
9491 if(!this.multiple && this.showToggleBtn){
9497 if (this.caret != false) {
9500 cls: 'fa fa-' + this.caret
9507 cls : 'input-group-addon btn dropdown-toggle',
9512 cls: 'combobox-clear',
9526 combobox.cls += ' roo-select2-container-multi';
9529 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9531 // Roo.log("left and has label");
9535 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9536 tooltip : 'This field is required'
9541 cls : 'control-label col-sm-' + this.labelWidth,
9542 html : this.fieldLabel
9546 cls : "col-sm-" + (12 - this.labelWidth),
9554 if(this.indicatorpos == 'right'){
9559 cls : 'control-label col-sm-' + this.labelWidth,
9560 html : this.fieldLabel
9565 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9566 tooltip : 'This field is required'
9569 cls : "col-sm-" + (12 - this.labelWidth),
9578 } else if ( this.fieldLabel.length) {
9579 // Roo.log(" label");
9583 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9584 tooltip : 'This field is required'
9588 //cls : 'input-group-addon',
9589 html : this.fieldLabel
9597 if(this.indicatorpos == 'right'){
9602 //cls : 'input-group-addon',
9603 html : this.fieldLabel
9608 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9609 tooltip : 'This field is required'
9620 // Roo.log(" no label && no align");
9627 ['xs','sm','md','lg'].map(function(size){
9628 if (settings[size]) {
9629 cfg.cls += ' col-' + size + '-' + settings[size];
9640 onResize : function(w, h){
9641 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9642 // if(typeof w == 'number'){
9643 // var x = w - this.trigger.getWidth();
9644 // this.inputEl().setWidth(this.adjustWidth('input', x));
9645 // this.trigger.setStyle('left', x+'px');
9650 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9653 getResizeEl : function(){
9654 return this.inputEl();
9658 getPositionEl : function(){
9659 return this.inputEl();
9663 alignErrorIcon : function(){
9664 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9668 initEvents : function(){
9672 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9673 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9674 if(!this.multiple && this.showToggleBtn){
9675 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9676 if(this.hideTrigger){
9677 this.trigger.setDisplayed(false);
9679 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9683 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9686 if(this.removable && !this.editable && !this.tickable){
9687 var close = this.closeTriggerEl();
9690 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9691 close.on('click', this.removeBtnClick, this, close);
9695 //this.trigger.addClassOnOver('x-form-trigger-over');
9696 //this.trigger.addClassOnClick('x-form-trigger-click');
9699 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9703 closeTriggerEl : function()
9705 var close = this.el.select('.roo-combo-removable-btn', true).first();
9706 return close ? close : false;
9709 removeBtnClick : function(e, h, el)
9713 if(this.fireEvent("remove", this) !== false){
9715 this.fireEvent("afterremove", this)
9719 createList : function()
9721 this.list = Roo.get(document.body).createChild({
9723 cls: 'typeahead typeahead-long dropdown-menu',
9724 style: 'display:none'
9727 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9732 initTrigger : function(){
9737 onDestroy : function(){
9739 this.trigger.removeAllListeners();
9740 // this.trigger.remove();
9743 // this.wrap.remove();
9745 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9749 onFocus : function(){
9750 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9753 this.wrap.addClass('x-trigger-wrap-focus');
9754 this.mimicing = true;
9755 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9756 if(this.monitorTab){
9757 this.el.on("keydown", this.checkTab, this);
9764 checkTab : function(e){
9765 if(e.getKey() == e.TAB){
9771 onBlur : function(){
9776 mimicBlur : function(e, t){
9778 if(!this.wrap.contains(t) && this.validateBlur()){
9785 triggerBlur : function(){
9786 this.mimicing = false;
9787 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9788 if(this.monitorTab){
9789 this.el.un("keydown", this.checkTab, this);
9791 //this.wrap.removeClass('x-trigger-wrap-focus');
9792 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9796 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9797 validateBlur : function(e, t){
9802 onDisable : function(){
9803 this.inputEl().dom.disabled = true;
9804 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9806 // this.wrap.addClass('x-item-disabled');
9811 onEnable : function(){
9812 this.inputEl().dom.disabled = false;
9813 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9815 // this.el.removeClass('x-item-disabled');
9820 onShow : function(){
9821 var ae = this.getActionEl();
9824 ae.dom.style.display = '';
9825 ae.dom.style.visibility = 'visible';
9831 onHide : function(){
9832 var ae = this.getActionEl();
9833 ae.dom.style.display = 'none';
9837 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9838 * by an implementing function.
9840 * @param {EventObject} e
9842 onTriggerClick : Roo.emptyFn
9846 * Ext JS Library 1.1.1
9847 * Copyright(c) 2006-2007, Ext JS, LLC.
9849 * Originally Released Under LGPL - original licence link has changed is not relivant.
9852 * <script type="text/javascript">
9857 * @class Roo.data.SortTypes
9859 * Defines the default sorting (casting?) comparison functions used when sorting data.
9861 Roo.data.SortTypes = {
9863 * Default sort that does nothing
9864 * @param {Mixed} s The value being converted
9865 * @return {Mixed} The comparison value
9872 * The regular expression used to strip tags
9876 stripTagsRE : /<\/?[^>]+>/gi,
9879 * Strips all HTML tags to sort on text only
9880 * @param {Mixed} s The value being converted
9881 * @return {String} The comparison value
9883 asText : function(s){
9884 return String(s).replace(this.stripTagsRE, "");
9888 * Strips all HTML tags to sort on text only - Case insensitive
9889 * @param {Mixed} s The value being converted
9890 * @return {String} The comparison value
9892 asUCText : function(s){
9893 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9897 * Case insensitive string
9898 * @param {Mixed} s The value being converted
9899 * @return {String} The comparison value
9901 asUCString : function(s) {
9902 return String(s).toUpperCase();
9907 * @param {Mixed} s The value being converted
9908 * @return {Number} The comparison value
9910 asDate : function(s) {
9914 if(s instanceof Date){
9917 return Date.parse(String(s));
9922 * @param {Mixed} s The value being converted
9923 * @return {Float} The comparison value
9925 asFloat : function(s) {
9926 var val = parseFloat(String(s).replace(/,/g, ""));
9935 * @param {Mixed} s The value being converted
9936 * @return {Number} The comparison value
9938 asInt : function(s) {
9939 var val = parseInt(String(s).replace(/,/g, ""));
9947 * Ext JS Library 1.1.1
9948 * Copyright(c) 2006-2007, Ext JS, LLC.
9950 * Originally Released Under LGPL - original licence link has changed is not relivant.
9953 * <script type="text/javascript">
9957 * @class Roo.data.Record
9958 * Instances of this class encapsulate both record <em>definition</em> information, and record
9959 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9960 * to access Records cached in an {@link Roo.data.Store} object.<br>
9962 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9963 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9966 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9968 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9969 * {@link #create}. The parameters are the same.
9970 * @param {Array} data An associative Array of data values keyed by the field name.
9971 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9972 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9973 * not specified an integer id is generated.
9975 Roo.data.Record = function(data, id){
9976 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9981 * Generate a constructor for a specific record layout.
9982 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9983 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9984 * Each field definition object may contain the following properties: <ul>
9985 * <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,
9986 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9987 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9988 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9989 * is being used, then this is a string containing the javascript expression to reference the data relative to
9990 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9991 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9992 * this may be omitted.</p></li>
9993 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9994 * <ul><li>auto (Default, implies no conversion)</li>
9999 * <li>date</li></ul></p></li>
10000 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10001 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10002 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10003 * by the Reader into an object that will be stored in the Record. It is passed the
10004 * following parameters:<ul>
10005 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10007 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10009 * <br>usage:<br><pre><code>
10010 var TopicRecord = Roo.data.Record.create(
10011 {name: 'title', mapping: 'topic_title'},
10012 {name: 'author', mapping: 'username'},
10013 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10014 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10015 {name: 'lastPoster', mapping: 'user2'},
10016 {name: 'excerpt', mapping: 'post_text'}
10019 var myNewRecord = new TopicRecord({
10020 title: 'Do my job please',
10023 lastPost: new Date(),
10024 lastPoster: 'Animal',
10025 excerpt: 'No way dude!'
10027 myStore.add(myNewRecord);
10032 Roo.data.Record.create = function(o){
10033 var f = function(){
10034 f.superclass.constructor.apply(this, arguments);
10036 Roo.extend(f, Roo.data.Record);
10037 var p = f.prototype;
10038 p.fields = new Roo.util.MixedCollection(false, function(field){
10041 for(var i = 0, len = o.length; i < len; i++){
10042 p.fields.add(new Roo.data.Field(o[i]));
10044 f.getField = function(name){
10045 return p.fields.get(name);
10050 Roo.data.Record.AUTO_ID = 1000;
10051 Roo.data.Record.EDIT = 'edit';
10052 Roo.data.Record.REJECT = 'reject';
10053 Roo.data.Record.COMMIT = 'commit';
10055 Roo.data.Record.prototype = {
10057 * Readonly flag - true if this record has been modified.
10066 join : function(store){
10067 this.store = store;
10071 * Set the named field to the specified value.
10072 * @param {String} name The name of the field to set.
10073 * @param {Object} value The value to set the field to.
10075 set : function(name, value){
10076 if(this.data[name] == value){
10080 if(!this.modified){
10081 this.modified = {};
10083 if(typeof this.modified[name] == 'undefined'){
10084 this.modified[name] = this.data[name];
10086 this.data[name] = value;
10087 if(!this.editing && this.store){
10088 this.store.afterEdit(this);
10093 * Get the value of the named field.
10094 * @param {String} name The name of the field to get the value of.
10095 * @return {Object} The value of the field.
10097 get : function(name){
10098 return this.data[name];
10102 beginEdit : function(){
10103 this.editing = true;
10104 this.modified = {};
10108 cancelEdit : function(){
10109 this.editing = false;
10110 delete this.modified;
10114 endEdit : function(){
10115 this.editing = false;
10116 if(this.dirty && this.store){
10117 this.store.afterEdit(this);
10122 * Usually called by the {@link Roo.data.Store} which owns the Record.
10123 * Rejects all changes made to the Record since either creation, or the last commit operation.
10124 * Modified fields are reverted to their original values.
10126 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10127 * of reject operations.
10129 reject : function(){
10130 var m = this.modified;
10132 if(typeof m[n] != "function"){
10133 this.data[n] = m[n];
10136 this.dirty = false;
10137 delete this.modified;
10138 this.editing = false;
10140 this.store.afterReject(this);
10145 * Usually called by the {@link Roo.data.Store} which owns the Record.
10146 * Commits all changes made to the Record since either creation, or the last commit operation.
10148 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10149 * of commit operations.
10151 commit : function(){
10152 this.dirty = false;
10153 delete this.modified;
10154 this.editing = false;
10156 this.store.afterCommit(this);
10161 hasError : function(){
10162 return this.error != null;
10166 clearError : function(){
10171 * Creates a copy of this record.
10172 * @param {String} id (optional) A new record id if you don't want to use this record's id
10175 copy : function(newId) {
10176 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10180 * Ext JS Library 1.1.1
10181 * Copyright(c) 2006-2007, Ext JS, LLC.
10183 * Originally Released Under LGPL - original licence link has changed is not relivant.
10186 * <script type="text/javascript">
10192 * @class Roo.data.Store
10193 * @extends Roo.util.Observable
10194 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10195 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10197 * 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
10198 * has no knowledge of the format of the data returned by the Proxy.<br>
10200 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10201 * instances from the data object. These records are cached and made available through accessor functions.
10203 * Creates a new Store.
10204 * @param {Object} config A config object containing the objects needed for the Store to access data,
10205 * and read the data into Records.
10207 Roo.data.Store = function(config){
10208 this.data = new Roo.util.MixedCollection(false);
10209 this.data.getKey = function(o){
10212 this.baseParams = {};
10214 this.paramNames = {
10219 "multisort" : "_multisort"
10222 if(config && config.data){
10223 this.inlineData = config.data;
10224 delete config.data;
10227 Roo.apply(this, config);
10229 if(this.reader){ // reader passed
10230 this.reader = Roo.factory(this.reader, Roo.data);
10231 this.reader.xmodule = this.xmodule || false;
10232 if(!this.recordType){
10233 this.recordType = this.reader.recordType;
10235 if(this.reader.onMetaChange){
10236 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10240 if(this.recordType){
10241 this.fields = this.recordType.prototype.fields;
10243 this.modified = [];
10247 * @event datachanged
10248 * Fires when the data cache has changed, and a widget which is using this Store
10249 * as a Record cache should refresh its view.
10250 * @param {Store} this
10252 datachanged : true,
10254 * @event metachange
10255 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10256 * @param {Store} this
10257 * @param {Object} meta The JSON metadata
10262 * Fires when Records have been added to the Store
10263 * @param {Store} this
10264 * @param {Roo.data.Record[]} records The array of Records added
10265 * @param {Number} index The index at which the record(s) were added
10270 * Fires when a Record has been removed from the Store
10271 * @param {Store} this
10272 * @param {Roo.data.Record} record The Record that was removed
10273 * @param {Number} index The index at which the record was removed
10278 * Fires when a Record has been updated
10279 * @param {Store} this
10280 * @param {Roo.data.Record} record The Record that was updated
10281 * @param {String} operation The update operation being performed. Value may be one of:
10283 Roo.data.Record.EDIT
10284 Roo.data.Record.REJECT
10285 Roo.data.Record.COMMIT
10291 * Fires when the data cache has been cleared.
10292 * @param {Store} this
10296 * @event beforeload
10297 * Fires before a request is made for a new data object. If the beforeload handler returns false
10298 * the load action will be canceled.
10299 * @param {Store} this
10300 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10304 * @event beforeloadadd
10305 * Fires after a new set of Records has been loaded.
10306 * @param {Store} this
10307 * @param {Roo.data.Record[]} records The Records that were loaded
10308 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10310 beforeloadadd : true,
10313 * Fires after a new set of Records has been loaded, before they are added to the store.
10314 * @param {Store} this
10315 * @param {Roo.data.Record[]} records The Records that were loaded
10316 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10317 * @params {Object} return from reader
10321 * @event loadexception
10322 * Fires if an exception occurs in the Proxy during loading.
10323 * Called with the signature of the Proxy's "loadexception" event.
10324 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10327 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10328 * @param {Object} load options
10329 * @param {Object} jsonData from your request (normally this contains the Exception)
10331 loadexception : true
10335 this.proxy = Roo.factory(this.proxy, Roo.data);
10336 this.proxy.xmodule = this.xmodule || false;
10337 this.relayEvents(this.proxy, ["loadexception"]);
10339 this.sortToggle = {};
10340 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10342 Roo.data.Store.superclass.constructor.call(this);
10344 if(this.inlineData){
10345 this.loadData(this.inlineData);
10346 delete this.inlineData;
10350 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10352 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10353 * without a remote query - used by combo/forms at present.
10357 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10360 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10363 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10364 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10367 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10368 * on any HTTP request
10371 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10374 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10378 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10379 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10381 remoteSort : false,
10384 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10385 * loaded or when a record is removed. (defaults to false).
10387 pruneModifiedRecords : false,
10390 lastOptions : null,
10393 * Add Records to the Store and fires the add event.
10394 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10396 add : function(records){
10397 records = [].concat(records);
10398 for(var i = 0, len = records.length; i < len; i++){
10399 records[i].join(this);
10401 var index = this.data.length;
10402 this.data.addAll(records);
10403 this.fireEvent("add", this, records, index);
10407 * Remove a Record from the Store and fires the remove event.
10408 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10410 remove : function(record){
10411 var index = this.data.indexOf(record);
10412 this.data.removeAt(index);
10413 if(this.pruneModifiedRecords){
10414 this.modified.remove(record);
10416 this.fireEvent("remove", this, record, index);
10420 * Remove all Records from the Store and fires the clear event.
10422 removeAll : function(){
10424 if(this.pruneModifiedRecords){
10425 this.modified = [];
10427 this.fireEvent("clear", this);
10431 * Inserts Records to the Store at the given index and fires the add event.
10432 * @param {Number} index The start index at which to insert the passed Records.
10433 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10435 insert : function(index, records){
10436 records = [].concat(records);
10437 for(var i = 0, len = records.length; i < len; i++){
10438 this.data.insert(index, records[i]);
10439 records[i].join(this);
10441 this.fireEvent("add", this, records, index);
10445 * Get the index within the cache of the passed Record.
10446 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10447 * @return {Number} The index of the passed Record. Returns -1 if not found.
10449 indexOf : function(record){
10450 return this.data.indexOf(record);
10454 * Get the index within the cache of the Record with the passed id.
10455 * @param {String} id The id of the Record to find.
10456 * @return {Number} The index of the Record. Returns -1 if not found.
10458 indexOfId : function(id){
10459 return this.data.indexOfKey(id);
10463 * Get the Record with the specified id.
10464 * @param {String} id The id of the Record to find.
10465 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10467 getById : function(id){
10468 return this.data.key(id);
10472 * Get the Record at the specified index.
10473 * @param {Number} index The index of the Record to find.
10474 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10476 getAt : function(index){
10477 return this.data.itemAt(index);
10481 * Returns a range of Records between specified indices.
10482 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10483 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10484 * @return {Roo.data.Record[]} An array of Records
10486 getRange : function(start, end){
10487 return this.data.getRange(start, end);
10491 storeOptions : function(o){
10492 o = Roo.apply({}, o);
10495 this.lastOptions = o;
10499 * Loads the Record cache from the configured Proxy using the configured Reader.
10501 * If using remote paging, then the first load call must specify the <em>start</em>
10502 * and <em>limit</em> properties in the options.params property to establish the initial
10503 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10505 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10506 * and this call will return before the new data has been loaded. Perform any post-processing
10507 * in a callback function, or in a "load" event handler.</strong>
10509 * @param {Object} options An object containing properties which control loading options:<ul>
10510 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10511 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10512 * passed the following arguments:<ul>
10513 * <li>r : Roo.data.Record[]</li>
10514 * <li>options: Options object from the load call</li>
10515 * <li>success: Boolean success indicator</li></ul></li>
10516 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10517 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10520 load : function(options){
10521 options = options || {};
10522 if(this.fireEvent("beforeload", this, options) !== false){
10523 this.storeOptions(options);
10524 var p = Roo.apply(options.params || {}, this.baseParams);
10525 // if meta was not loaded from remote source.. try requesting it.
10526 if (!this.reader.metaFromRemote) {
10527 p._requestMeta = 1;
10529 if(this.sortInfo && this.remoteSort){
10530 var pn = this.paramNames;
10531 p[pn["sort"]] = this.sortInfo.field;
10532 p[pn["dir"]] = this.sortInfo.direction;
10534 if (this.multiSort) {
10535 var pn = this.paramNames;
10536 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10539 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10544 * Reloads the Record cache from the configured Proxy using the configured Reader and
10545 * the options from the last load operation performed.
10546 * @param {Object} options (optional) An object containing properties which may override the options
10547 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10548 * the most recently used options are reused).
10550 reload : function(options){
10551 this.load(Roo.applyIf(options||{}, this.lastOptions));
10555 // Called as a callback by the Reader during a load operation.
10556 loadRecords : function(o, options, success){
10557 if(!o || success === false){
10558 if(success !== false){
10559 this.fireEvent("load", this, [], options, o);
10561 if(options.callback){
10562 options.callback.call(options.scope || this, [], options, false);
10566 // if data returned failure - throw an exception.
10567 if (o.success === false) {
10568 // show a message if no listener is registered.
10569 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10570 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10572 // loadmask wil be hooked into this..
10573 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10576 var r = o.records, t = o.totalRecords || r.length;
10578 this.fireEvent("beforeloadadd", this, r, options, o);
10580 if(!options || options.add !== true){
10581 if(this.pruneModifiedRecords){
10582 this.modified = [];
10584 for(var i = 0, len = r.length; i < len; i++){
10588 this.data = this.snapshot;
10589 delete this.snapshot;
10592 this.data.addAll(r);
10593 this.totalLength = t;
10595 this.fireEvent("datachanged", this);
10597 this.totalLength = Math.max(t, this.data.length+r.length);
10600 this.fireEvent("load", this, r, options, o);
10601 if(options.callback){
10602 options.callback.call(options.scope || this, r, options, true);
10608 * Loads data from a passed data block. A Reader which understands the format of the data
10609 * must have been configured in the constructor.
10610 * @param {Object} data The data block from which to read the Records. The format of the data expected
10611 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10612 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10614 loadData : function(o, append){
10615 var r = this.reader.readRecords(o);
10616 this.loadRecords(r, {add: append}, true);
10620 * Gets the number of cached records.
10622 * <em>If using paging, this may not be the total size of the dataset. If the data object
10623 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10624 * the data set size</em>
10626 getCount : function(){
10627 return this.data.length || 0;
10631 * Gets the total number of records in the dataset as returned by the server.
10633 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10634 * the dataset size</em>
10636 getTotalCount : function(){
10637 return this.totalLength || 0;
10641 * Returns the sort state of the Store as an object with two properties:
10643 field {String} The name of the field by which the Records are sorted
10644 direction {String} The sort order, "ASC" or "DESC"
10647 getSortState : function(){
10648 return this.sortInfo;
10652 applySort : function(){
10653 if(this.sortInfo && !this.remoteSort){
10654 var s = this.sortInfo, f = s.field;
10655 var st = this.fields.get(f).sortType;
10656 var fn = function(r1, r2){
10657 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10658 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10660 this.data.sort(s.direction, fn);
10661 if(this.snapshot && this.snapshot != this.data){
10662 this.snapshot.sort(s.direction, fn);
10668 * Sets the default sort column and order to be used by the next load operation.
10669 * @param {String} fieldName The name of the field to sort by.
10670 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10672 setDefaultSort : function(field, dir){
10673 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10677 * Sort the Records.
10678 * If remote sorting is used, the sort is performed on the server, and the cache is
10679 * reloaded. If local sorting is used, the cache is sorted internally.
10680 * @param {String} fieldName The name of the field to sort by.
10681 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10683 sort : function(fieldName, dir){
10684 var f = this.fields.get(fieldName);
10686 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10688 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10689 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10694 this.sortToggle[f.name] = dir;
10695 this.sortInfo = {field: f.name, direction: dir};
10696 if(!this.remoteSort){
10698 this.fireEvent("datachanged", this);
10700 this.load(this.lastOptions);
10705 * Calls the specified function for each of the Records in the cache.
10706 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10707 * Returning <em>false</em> aborts and exits the iteration.
10708 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10710 each : function(fn, scope){
10711 this.data.each(fn, scope);
10715 * Gets all records modified since the last commit. Modified records are persisted across load operations
10716 * (e.g., during paging).
10717 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10719 getModifiedRecords : function(){
10720 return this.modified;
10724 createFilterFn : function(property, value, anyMatch){
10725 if(!value.exec){ // not a regex
10726 value = String(value);
10727 if(value.length == 0){
10730 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10732 return function(r){
10733 return value.test(r.data[property]);
10738 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10739 * @param {String} property A field on your records
10740 * @param {Number} start The record index to start at (defaults to 0)
10741 * @param {Number} end The last record index to include (defaults to length - 1)
10742 * @return {Number} The sum
10744 sum : function(property, start, end){
10745 var rs = this.data.items, v = 0;
10746 start = start || 0;
10747 end = (end || end === 0) ? end : rs.length-1;
10749 for(var i = start; i <= end; i++){
10750 v += (rs[i].data[property] || 0);
10756 * Filter the records by a specified property.
10757 * @param {String} field A field on your records
10758 * @param {String/RegExp} value Either a string that the field
10759 * should start with or a RegExp to test against the field
10760 * @param {Boolean} anyMatch True to match any part not just the beginning
10762 filter : function(property, value, anyMatch){
10763 var fn = this.createFilterFn(property, value, anyMatch);
10764 return fn ? this.filterBy(fn) : this.clearFilter();
10768 * Filter by a function. The specified function will be called with each
10769 * record in this data source. If the function returns true the record is included,
10770 * otherwise it is filtered.
10771 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10772 * @param {Object} scope (optional) The scope of the function (defaults to this)
10774 filterBy : function(fn, scope){
10775 this.snapshot = this.snapshot || this.data;
10776 this.data = this.queryBy(fn, scope||this);
10777 this.fireEvent("datachanged", this);
10781 * Query the records by a specified property.
10782 * @param {String} field A field on your records
10783 * @param {String/RegExp} value Either a string that the field
10784 * should start with or a RegExp to test against the field
10785 * @param {Boolean} anyMatch True to match any part not just the beginning
10786 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10788 query : function(property, value, anyMatch){
10789 var fn = this.createFilterFn(property, value, anyMatch);
10790 return fn ? this.queryBy(fn) : this.data.clone();
10794 * Query by a function. The specified function will be called with each
10795 * record in this data source. If the function returns true the record is included
10797 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10798 * @param {Object} scope (optional) The scope of the function (defaults to this)
10799 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10801 queryBy : function(fn, scope){
10802 var data = this.snapshot || this.data;
10803 return data.filterBy(fn, scope||this);
10807 * Collects unique values for a particular dataIndex from this store.
10808 * @param {String} dataIndex The property to collect
10809 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10810 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10811 * @return {Array} An array of the unique values
10813 collect : function(dataIndex, allowNull, bypassFilter){
10814 var d = (bypassFilter === true && this.snapshot) ?
10815 this.snapshot.items : this.data.items;
10816 var v, sv, r = [], l = {};
10817 for(var i = 0, len = d.length; i < len; i++){
10818 v = d[i].data[dataIndex];
10820 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10829 * Revert to a view of the Record cache with no filtering applied.
10830 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10832 clearFilter : function(suppressEvent){
10833 if(this.snapshot && this.snapshot != this.data){
10834 this.data = this.snapshot;
10835 delete this.snapshot;
10836 if(suppressEvent !== true){
10837 this.fireEvent("datachanged", this);
10843 afterEdit : function(record){
10844 if(this.modified.indexOf(record) == -1){
10845 this.modified.push(record);
10847 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10851 afterReject : function(record){
10852 this.modified.remove(record);
10853 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10857 afterCommit : function(record){
10858 this.modified.remove(record);
10859 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10863 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10864 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10866 commitChanges : function(){
10867 var m = this.modified.slice(0);
10868 this.modified = [];
10869 for(var i = 0, len = m.length; i < len; i++){
10875 * Cancel outstanding changes on all changed records.
10877 rejectChanges : function(){
10878 var m = this.modified.slice(0);
10879 this.modified = [];
10880 for(var i = 0, len = m.length; i < len; i++){
10885 onMetaChange : function(meta, rtype, o){
10886 this.recordType = rtype;
10887 this.fields = rtype.prototype.fields;
10888 delete this.snapshot;
10889 this.sortInfo = meta.sortInfo || this.sortInfo;
10890 this.modified = [];
10891 this.fireEvent('metachange', this, this.reader.meta);
10894 moveIndex : function(data, type)
10896 var index = this.indexOf(data);
10898 var newIndex = index + type;
10902 this.insert(newIndex, data);
10907 * Ext JS Library 1.1.1
10908 * Copyright(c) 2006-2007, Ext JS, LLC.
10910 * Originally Released Under LGPL - original licence link has changed is not relivant.
10913 * <script type="text/javascript">
10917 * @class Roo.data.SimpleStore
10918 * @extends Roo.data.Store
10919 * Small helper class to make creating Stores from Array data easier.
10920 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10921 * @cfg {Array} fields An array of field definition objects, or field name strings.
10922 * @cfg {Array} data The multi-dimensional array of data
10924 * @param {Object} config
10926 Roo.data.SimpleStore = function(config){
10927 Roo.data.SimpleStore.superclass.constructor.call(this, {
10929 reader: new Roo.data.ArrayReader({
10932 Roo.data.Record.create(config.fields)
10934 proxy : new Roo.data.MemoryProxy(config.data)
10938 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10940 * Ext JS Library 1.1.1
10941 * Copyright(c) 2006-2007, Ext JS, LLC.
10943 * Originally Released Under LGPL - original licence link has changed is not relivant.
10946 * <script type="text/javascript">
10951 * @extends Roo.data.Store
10952 * @class Roo.data.JsonStore
10953 * Small helper class to make creating Stores for JSON data easier. <br/>
10955 var store = new Roo.data.JsonStore({
10956 url: 'get-images.php',
10958 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10961 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10962 * JsonReader and HttpProxy (unless inline data is provided).</b>
10963 * @cfg {Array} fields An array of field definition objects, or field name strings.
10965 * @param {Object} config
10967 Roo.data.JsonStore = function(c){
10968 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10969 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10970 reader: new Roo.data.JsonReader(c, c.fields)
10973 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10975 * Ext JS Library 1.1.1
10976 * Copyright(c) 2006-2007, Ext JS, LLC.
10978 * Originally Released Under LGPL - original licence link has changed is not relivant.
10981 * <script type="text/javascript">
10985 Roo.data.Field = function(config){
10986 if(typeof config == "string"){
10987 config = {name: config};
10989 Roo.apply(this, config);
10992 this.type = "auto";
10995 var st = Roo.data.SortTypes;
10996 // named sortTypes are supported, here we look them up
10997 if(typeof this.sortType == "string"){
10998 this.sortType = st[this.sortType];
11001 // set default sortType for strings and dates
11002 if(!this.sortType){
11005 this.sortType = st.asUCString;
11008 this.sortType = st.asDate;
11011 this.sortType = st.none;
11016 var stripRe = /[\$,%]/g;
11018 // prebuilt conversion function for this field, instead of
11019 // switching every time we're reading a value
11021 var cv, dateFormat = this.dateFormat;
11026 cv = function(v){ return v; };
11029 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11033 return v !== undefined && v !== null && v !== '' ?
11034 parseInt(String(v).replace(stripRe, ""), 10) : '';
11039 return v !== undefined && v !== null && v !== '' ?
11040 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11045 cv = function(v){ return v === true || v === "true" || v == 1; };
11052 if(v instanceof Date){
11056 if(dateFormat == "timestamp"){
11057 return new Date(v*1000);
11059 return Date.parseDate(v, dateFormat);
11061 var parsed = Date.parse(v);
11062 return parsed ? new Date(parsed) : null;
11071 Roo.data.Field.prototype = {
11079 * Ext JS Library 1.1.1
11080 * Copyright(c) 2006-2007, Ext JS, LLC.
11082 * Originally Released Under LGPL - original licence link has changed is not relivant.
11085 * <script type="text/javascript">
11088 // Base class for reading structured data from a data source. This class is intended to be
11089 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11092 * @class Roo.data.DataReader
11093 * Base class for reading structured data from a data source. This class is intended to be
11094 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11097 Roo.data.DataReader = function(meta, recordType){
11101 this.recordType = recordType instanceof Array ?
11102 Roo.data.Record.create(recordType) : recordType;
11105 Roo.data.DataReader.prototype = {
11107 * Create an empty record
11108 * @param {Object} data (optional) - overlay some values
11109 * @return {Roo.data.Record} record created.
11111 newRow : function(d) {
11113 this.recordType.prototype.fields.each(function(c) {
11115 case 'int' : da[c.name] = 0; break;
11116 case 'date' : da[c.name] = new Date(); break;
11117 case 'float' : da[c.name] = 0.0; break;
11118 case 'boolean' : da[c.name] = false; break;
11119 default : da[c.name] = ""; break;
11123 return new this.recordType(Roo.apply(da, d));
11128 * Ext JS Library 1.1.1
11129 * Copyright(c) 2006-2007, Ext JS, LLC.
11131 * Originally Released Under LGPL - original licence link has changed is not relivant.
11134 * <script type="text/javascript">
11138 * @class Roo.data.DataProxy
11139 * @extends Roo.data.Observable
11140 * This class is an abstract base class for implementations which provide retrieval of
11141 * unformatted data objects.<br>
11143 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11144 * (of the appropriate type which knows how to parse the data object) to provide a block of
11145 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11147 * Custom implementations must implement the load method as described in
11148 * {@link Roo.data.HttpProxy#load}.
11150 Roo.data.DataProxy = function(){
11153 * @event beforeload
11154 * Fires before a network request is made to retrieve a data object.
11155 * @param {Object} This DataProxy object.
11156 * @param {Object} params The params parameter to the load function.
11161 * Fires before the load method's callback is called.
11162 * @param {Object} This DataProxy object.
11163 * @param {Object} o The data object.
11164 * @param {Object} arg The callback argument object passed to the load function.
11168 * @event loadexception
11169 * Fires if an Exception occurs during data retrieval.
11170 * @param {Object} This DataProxy object.
11171 * @param {Object} o The data object.
11172 * @param {Object} arg The callback argument object passed to the load function.
11173 * @param {Object} e The Exception.
11175 loadexception : true
11177 Roo.data.DataProxy.superclass.constructor.call(this);
11180 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11183 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11187 * Ext JS Library 1.1.1
11188 * Copyright(c) 2006-2007, Ext JS, LLC.
11190 * Originally Released Under LGPL - original licence link has changed is not relivant.
11193 * <script type="text/javascript">
11196 * @class Roo.data.MemoryProxy
11197 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11198 * to the Reader when its load method is called.
11200 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11202 Roo.data.MemoryProxy = function(data){
11206 Roo.data.MemoryProxy.superclass.constructor.call(this);
11210 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11213 * Load data from the requested source (in this case an in-memory
11214 * data object passed to the constructor), read the data object into
11215 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11216 * process that block using the passed callback.
11217 * @param {Object} params This parameter is not used by the MemoryProxy class.
11218 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11219 * object into a block of Roo.data.Records.
11220 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11221 * The function must be passed <ul>
11222 * <li>The Record block object</li>
11223 * <li>The "arg" argument from the load function</li>
11224 * <li>A boolean success indicator</li>
11226 * @param {Object} scope The scope in which to call the callback
11227 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11229 load : function(params, reader, callback, scope, arg){
11230 params = params || {};
11233 result = reader.readRecords(this.data);
11235 this.fireEvent("loadexception", this, arg, null, e);
11236 callback.call(scope, null, arg, false);
11239 callback.call(scope, result, arg, true);
11243 update : function(params, records){
11248 * Ext JS Library 1.1.1
11249 * Copyright(c) 2006-2007, Ext JS, LLC.
11251 * Originally Released Under LGPL - original licence link has changed is not relivant.
11254 * <script type="text/javascript">
11257 * @class Roo.data.HttpProxy
11258 * @extends Roo.data.DataProxy
11259 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11260 * configured to reference a certain URL.<br><br>
11262 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11263 * from which the running page was served.<br><br>
11265 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11267 * Be aware that to enable the browser to parse an XML document, the server must set
11268 * the Content-Type header in the HTTP response to "text/xml".
11270 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11271 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11272 * will be used to make the request.
11274 Roo.data.HttpProxy = function(conn){
11275 Roo.data.HttpProxy.superclass.constructor.call(this);
11276 // is conn a conn config or a real conn?
11278 this.useAjax = !conn || !conn.events;
11282 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11283 // thse are take from connection...
11286 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11289 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11290 * extra parameters to each request made by this object. (defaults to undefined)
11293 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11294 * to each request made by this object. (defaults to undefined)
11297 * @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)
11300 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11303 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11309 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11313 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11314 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11315 * a finer-grained basis than the DataProxy events.
11317 getConnection : function(){
11318 return this.useAjax ? Roo.Ajax : this.conn;
11322 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11323 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11324 * process that block using the passed callback.
11325 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11326 * for the request to the remote server.
11327 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11328 * object into a block of Roo.data.Records.
11329 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11330 * The function must be passed <ul>
11331 * <li>The Record block object</li>
11332 * <li>The "arg" argument from the load function</li>
11333 * <li>A boolean success indicator</li>
11335 * @param {Object} scope The scope in which to call the callback
11336 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11338 load : function(params, reader, callback, scope, arg){
11339 if(this.fireEvent("beforeload", this, params) !== false){
11341 params : params || {},
11343 callback : callback,
11348 callback : this.loadResponse,
11352 Roo.applyIf(o, this.conn);
11353 if(this.activeRequest){
11354 Roo.Ajax.abort(this.activeRequest);
11356 this.activeRequest = Roo.Ajax.request(o);
11358 this.conn.request(o);
11361 callback.call(scope||this, null, arg, false);
11366 loadResponse : function(o, success, response){
11367 delete this.activeRequest;
11369 this.fireEvent("loadexception", this, o, response);
11370 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11375 result = o.reader.read(response);
11377 this.fireEvent("loadexception", this, o, response, e);
11378 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11382 this.fireEvent("load", this, o, o.request.arg);
11383 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11387 update : function(dataSet){
11392 updateResponse : function(dataSet){
11397 * Ext JS Library 1.1.1
11398 * Copyright(c) 2006-2007, Ext JS, LLC.
11400 * Originally Released Under LGPL - original licence link has changed is not relivant.
11403 * <script type="text/javascript">
11407 * @class Roo.data.ScriptTagProxy
11408 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11409 * other than the originating domain of the running page.<br><br>
11411 * <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
11412 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11414 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11415 * source code that is used as the source inside a <script> tag.<br><br>
11417 * In order for the browser to process the returned data, the server must wrap the data object
11418 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11419 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11420 * depending on whether the callback name was passed:
11423 boolean scriptTag = false;
11424 String cb = request.getParameter("callback");
11427 response.setContentType("text/javascript");
11429 response.setContentType("application/x-json");
11431 Writer out = response.getWriter();
11433 out.write(cb + "(");
11435 out.print(dataBlock.toJsonString());
11442 * @param {Object} config A configuration object.
11444 Roo.data.ScriptTagProxy = function(config){
11445 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11446 Roo.apply(this, config);
11447 this.head = document.getElementsByTagName("head")[0];
11450 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11452 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11454 * @cfg {String} url The URL from which to request the data object.
11457 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11461 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11462 * the server the name of the callback function set up by the load call to process the returned data object.
11463 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11464 * javascript output which calls this named function passing the data object as its only parameter.
11466 callbackParam : "callback",
11468 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11469 * name to the request.
11474 * Load data from the configured URL, read the data object into
11475 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11476 * process that block using the passed callback.
11477 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11478 * for the request to the remote server.
11479 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11480 * object into a block of Roo.data.Records.
11481 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11482 * The function must be passed <ul>
11483 * <li>The Record block object</li>
11484 * <li>The "arg" argument from the load function</li>
11485 * <li>A boolean success indicator</li>
11487 * @param {Object} scope The scope in which to call the callback
11488 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11490 load : function(params, reader, callback, scope, arg){
11491 if(this.fireEvent("beforeload", this, params) !== false){
11493 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11495 var url = this.url;
11496 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11498 url += "&_dc=" + (new Date().getTime());
11500 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11503 cb : "stcCallback"+transId,
11504 scriptId : "stcScript"+transId,
11508 callback : callback,
11514 window[trans.cb] = function(o){
11515 conn.handleResponse(o, trans);
11518 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11520 if(this.autoAbort !== false){
11524 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11526 var script = document.createElement("script");
11527 script.setAttribute("src", url);
11528 script.setAttribute("type", "text/javascript");
11529 script.setAttribute("id", trans.scriptId);
11530 this.head.appendChild(script);
11532 this.trans = trans;
11534 callback.call(scope||this, null, arg, false);
11539 isLoading : function(){
11540 return this.trans ? true : false;
11544 * Abort the current server request.
11546 abort : function(){
11547 if(this.isLoading()){
11548 this.destroyTrans(this.trans);
11553 destroyTrans : function(trans, isLoaded){
11554 this.head.removeChild(document.getElementById(trans.scriptId));
11555 clearTimeout(trans.timeoutId);
11557 window[trans.cb] = undefined;
11559 delete window[trans.cb];
11562 // if hasn't been loaded, wait for load to remove it to prevent script error
11563 window[trans.cb] = function(){
11564 window[trans.cb] = undefined;
11566 delete window[trans.cb];
11573 handleResponse : function(o, trans){
11574 this.trans = false;
11575 this.destroyTrans(trans, true);
11578 result = trans.reader.readRecords(o);
11580 this.fireEvent("loadexception", this, o, trans.arg, e);
11581 trans.callback.call(trans.scope||window, null, trans.arg, false);
11584 this.fireEvent("load", this, o, trans.arg);
11585 trans.callback.call(trans.scope||window, result, trans.arg, true);
11589 handleFailure : function(trans){
11590 this.trans = false;
11591 this.destroyTrans(trans, false);
11592 this.fireEvent("loadexception", this, null, trans.arg);
11593 trans.callback.call(trans.scope||window, null, trans.arg, false);
11597 * Ext JS Library 1.1.1
11598 * Copyright(c) 2006-2007, Ext JS, LLC.
11600 * Originally Released Under LGPL - original licence link has changed is not relivant.
11603 * <script type="text/javascript">
11607 * @class Roo.data.JsonReader
11608 * @extends Roo.data.DataReader
11609 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11610 * based on mappings in a provided Roo.data.Record constructor.
11612 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11613 * in the reply previously.
11618 var RecordDef = Roo.data.Record.create([
11619 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11620 {name: 'occupation'} // This field will use "occupation" as the mapping.
11622 var myReader = new Roo.data.JsonReader({
11623 totalProperty: "results", // The property which contains the total dataset size (optional)
11624 root: "rows", // The property which contains an Array of row objects
11625 id: "id" // The property within each row object that provides an ID for the record (optional)
11629 * This would consume a JSON file like this:
11631 { 'results': 2, 'rows': [
11632 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11633 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11636 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11637 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11638 * paged from the remote server.
11639 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11640 * @cfg {String} root name of the property which contains the Array of row objects.
11641 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11642 * @cfg {Array} fields Array of field definition objects
11644 * Create a new JsonReader
11645 * @param {Object} meta Metadata configuration options
11646 * @param {Object} recordType Either an Array of field definition objects,
11647 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11649 Roo.data.JsonReader = function(meta, recordType){
11652 // set some defaults:
11653 Roo.applyIf(meta, {
11654 totalProperty: 'total',
11655 successProperty : 'success',
11660 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11662 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11665 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11666 * Used by Store query builder to append _requestMeta to params.
11669 metaFromRemote : false,
11671 * This method is only used by a DataProxy which has retrieved data from a remote server.
11672 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11673 * @return {Object} data A data block which is used by an Roo.data.Store object as
11674 * a cache of Roo.data.Records.
11676 read : function(response){
11677 var json = response.responseText;
11679 var o = /* eval:var:o */ eval("("+json+")");
11681 throw {message: "JsonReader.read: Json object not found"};
11687 this.metaFromRemote = true;
11688 this.meta = o.metaData;
11689 this.recordType = Roo.data.Record.create(o.metaData.fields);
11690 this.onMetaChange(this.meta, this.recordType, o);
11692 return this.readRecords(o);
11695 // private function a store will implement
11696 onMetaChange : function(meta, recordType, o){
11703 simpleAccess: function(obj, subsc) {
11710 getJsonAccessor: function(){
11712 return function(expr) {
11714 return(re.test(expr))
11715 ? new Function("obj", "return obj." + expr)
11720 return Roo.emptyFn;
11725 * Create a data block containing Roo.data.Records from an XML document.
11726 * @param {Object} o An object which contains an Array of row objects in the property specified
11727 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11728 * which contains the total size of the dataset.
11729 * @return {Object} data A data block which is used by an Roo.data.Store object as
11730 * a cache of Roo.data.Records.
11732 readRecords : function(o){
11734 * After any data loads, the raw JSON data is available for further custom processing.
11738 var s = this.meta, Record = this.recordType,
11739 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11741 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11743 if(s.totalProperty) {
11744 this.getTotal = this.getJsonAccessor(s.totalProperty);
11746 if(s.successProperty) {
11747 this.getSuccess = this.getJsonAccessor(s.successProperty);
11749 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11751 var g = this.getJsonAccessor(s.id);
11752 this.getId = function(rec) {
11754 return (r === undefined || r === "") ? null : r;
11757 this.getId = function(){return null;};
11760 for(var jj = 0; jj < fl; jj++){
11762 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11763 this.ef[jj] = this.getJsonAccessor(map);
11767 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11768 if(s.totalProperty){
11769 var vt = parseInt(this.getTotal(o), 10);
11774 if(s.successProperty){
11775 var vs = this.getSuccess(o);
11776 if(vs === false || vs === 'false'){
11781 for(var i = 0; i < c; i++){
11784 var id = this.getId(n);
11785 for(var j = 0; j < fl; j++){
11787 var v = this.ef[j](n);
11789 Roo.log('missing convert for ' + f.name);
11793 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11795 var record = new Record(values, id);
11797 records[i] = record;
11803 totalRecords : totalRecords
11808 * Ext JS Library 1.1.1
11809 * Copyright(c) 2006-2007, Ext JS, LLC.
11811 * Originally Released Under LGPL - original licence link has changed is not relivant.
11814 * <script type="text/javascript">
11818 * @class Roo.data.ArrayReader
11819 * @extends Roo.data.DataReader
11820 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11821 * Each element of that Array represents a row of data fields. The
11822 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11823 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11827 var RecordDef = Roo.data.Record.create([
11828 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11829 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11831 var myReader = new Roo.data.ArrayReader({
11832 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11836 * This would consume an Array like this:
11838 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11840 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11842 * Create a new JsonReader
11843 * @param {Object} meta Metadata configuration options.
11844 * @param {Object} recordType Either an Array of field definition objects
11845 * as specified to {@link Roo.data.Record#create},
11846 * or an {@link Roo.data.Record} object
11847 * created using {@link Roo.data.Record#create}.
11849 Roo.data.ArrayReader = function(meta, recordType){
11850 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11853 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11855 * Create a data block containing Roo.data.Records from an XML document.
11856 * @param {Object} o An Array of row objects which represents the dataset.
11857 * @return {Object} data A data block which is used by an Roo.data.Store object as
11858 * a cache of Roo.data.Records.
11860 readRecords : function(o){
11861 var sid = this.meta ? this.meta.id : null;
11862 var recordType = this.recordType, fields = recordType.prototype.fields;
11865 for(var i = 0; i < root.length; i++){
11868 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11869 for(var j = 0, jlen = fields.length; j < jlen; j++){
11870 var f = fields.items[j];
11871 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11872 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11874 values[f.name] = v;
11876 var record = new recordType(values, id);
11878 records[records.length] = record;
11882 totalRecords : records.length
11891 * @class Roo.bootstrap.ComboBox
11892 * @extends Roo.bootstrap.TriggerField
11893 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11894 * @cfg {Boolean} append (true|false) default false
11895 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11896 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11897 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11898 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11899 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11900 * @cfg {Boolean} animate default true
11901 * @cfg {Boolean} emptyResultText only for touch device
11902 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11904 * Create a new ComboBox.
11905 * @param {Object} config Configuration options
11907 Roo.bootstrap.ComboBox = function(config){
11908 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11912 * Fires when the dropdown list is expanded
11913 * @param {Roo.bootstrap.ComboBox} combo This combo box
11918 * Fires when the dropdown list is collapsed
11919 * @param {Roo.bootstrap.ComboBox} combo This combo box
11923 * @event beforeselect
11924 * Fires before a list item is selected. Return false to cancel the selection.
11925 * @param {Roo.bootstrap.ComboBox} combo This combo box
11926 * @param {Roo.data.Record} record The data record returned from the underlying store
11927 * @param {Number} index The index of the selected item in the dropdown list
11929 'beforeselect' : true,
11932 * Fires when a list item is selected
11933 * @param {Roo.bootstrap.ComboBox} combo This combo box
11934 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11935 * @param {Number} index The index of the selected item in the dropdown list
11939 * @event beforequery
11940 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11941 * The event object passed has these properties:
11942 * @param {Roo.bootstrap.ComboBox} combo This combo box
11943 * @param {String} query The query
11944 * @param {Boolean} forceAll true to force "all" query
11945 * @param {Boolean} cancel true to cancel the query
11946 * @param {Object} e The query event object
11948 'beforequery': true,
11951 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11952 * @param {Roo.bootstrap.ComboBox} combo This combo box
11957 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11958 * @param {Roo.bootstrap.ComboBox} combo This combo box
11959 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11964 * Fires when the remove value from the combobox array
11965 * @param {Roo.bootstrap.ComboBox} combo This combo box
11969 * @event afterremove
11970 * Fires when the remove value from the combobox array
11971 * @param {Roo.bootstrap.ComboBox} combo This combo box
11973 'afterremove' : true,
11975 * @event specialfilter
11976 * Fires when specialfilter
11977 * @param {Roo.bootstrap.ComboBox} combo This combo box
11979 'specialfilter' : true,
11982 * Fires when tick the element
11983 * @param {Roo.bootstrap.ComboBox} combo This combo box
11987 * @event touchviewdisplay
11988 * Fires when touch view require special display (default is using displayField)
11989 * @param {Roo.bootstrap.ComboBox} combo This combo box
11990 * @param {Object} cfg set html .
11992 'touchviewdisplay' : true
11997 this.tickItems = [];
11999 this.selectedIndex = -1;
12000 if(this.mode == 'local'){
12001 if(config.queryDelay === undefined){
12002 this.queryDelay = 10;
12004 if(config.minChars === undefined){
12010 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12013 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12014 * rendering into an Roo.Editor, defaults to false)
12017 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12018 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12021 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12024 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12025 * the dropdown list (defaults to undefined, with no header element)
12029 * @cfg {String/Roo.Template} tpl The template to use to render the output
12033 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12035 listWidth: undefined,
12037 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12038 * mode = 'remote' or 'text' if mode = 'local')
12040 displayField: undefined,
12043 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12044 * mode = 'remote' or 'value' if mode = 'local').
12045 * Note: use of a valueField requires the user make a selection
12046 * in order for a value to be mapped.
12048 valueField: undefined,
12050 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12055 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12056 * field's data value (defaults to the underlying DOM element's name)
12058 hiddenName: undefined,
12060 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12064 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12066 selectedClass: 'active',
12069 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12073 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12074 * anchor positions (defaults to 'tl-bl')
12076 listAlign: 'tl-bl?',
12078 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12082 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12083 * query specified by the allQuery config option (defaults to 'query')
12085 triggerAction: 'query',
12087 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12088 * (defaults to 4, does not apply if editable = false)
12092 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12093 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12097 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12098 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12102 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12103 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12107 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12108 * when editable = true (defaults to false)
12110 selectOnFocus:false,
12112 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12114 queryParam: 'query',
12116 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12117 * when mode = 'remote' (defaults to 'Loading...')
12119 loadingText: 'Loading...',
12121 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12125 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12129 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12130 * traditional select (defaults to true)
12134 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12138 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12142 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12143 * listWidth has a higher value)
12147 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12148 * allow the user to set arbitrary text into the field (defaults to false)
12150 forceSelection:false,
12152 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12153 * if typeAhead = true (defaults to 250)
12155 typeAheadDelay : 250,
12157 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12158 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12160 valueNotFoundText : undefined,
12162 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12164 blockFocus : false,
12167 * @cfg {Boolean} disableClear Disable showing of clear button.
12169 disableClear : false,
12171 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12173 alwaysQuery : false,
12176 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12181 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12183 invalidClass : "has-warning",
12186 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12188 validClass : "has-success",
12191 * @cfg {Boolean} specialFilter (true|false) special filter default false
12193 specialFilter : false,
12196 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12198 mobileTouchView : true,
12210 btnPosition : 'right',
12211 triggerList : true,
12212 showToggleBtn : true,
12214 emptyResultText: 'Empty',
12215 triggerText : 'Select',
12217 // element that contains real text value.. (when hidden is used..)
12219 getAutoCreate : function()
12227 if(Roo.isTouch && this.mobileTouchView){
12228 cfg = this.getAutoCreateTouchView();
12235 if(!this.tickable){
12236 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12241 * ComboBox with tickable selections
12244 var align = this.labelAlign || this.parentLabelAlign();
12247 cls : 'form-group roo-combobox-tickable' //input-group
12252 cls : 'tickable-buttons',
12257 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12258 html : this.triggerText
12264 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12271 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12278 buttons.cn.unshift({
12280 cls: 'roo-select2-search-field-input'
12286 Roo.each(buttons.cn, function(c){
12288 c.cls += ' btn-' + _this.size;
12291 if (_this.disabled) {
12302 cls: 'form-hidden-field'
12306 cls: 'roo-select2-choices',
12310 cls: 'roo-select2-search-field',
12322 cls: 'roo-select2-container input-group roo-select2-container-multi',
12327 // cls: 'typeahead typeahead-long dropdown-menu',
12328 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12333 if(this.hasFeedback && !this.allowBlank){
12337 cls: 'glyphicon form-control-feedback'
12340 combobox.cn.push(feedback);
12343 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12345 // Roo.log("left and has label");
12349 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12350 tooltip : 'This field is required'
12355 cls : 'control-label col-sm-' + this.labelWidth,
12356 html : this.fieldLabel
12360 cls : "col-sm-" + (12 - this.labelWidth),
12368 if(this.indicatorpos == 'right'){
12374 cls : 'control-label col-sm-' + this.labelWidth,
12375 html : this.fieldLabel
12380 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12381 tooltip : 'This field is required'
12384 cls : "col-sm-" + (12 - this.labelWidth),
12395 } else if ( this.fieldLabel.length) {
12396 // Roo.log(" label");
12400 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12401 tooltip : 'This field is required'
12405 //cls : 'input-group-addon',
12406 html : this.fieldLabel
12414 if(this.indicatorpos == 'right'){
12419 //cls : 'input-group-addon',
12420 html : this.fieldLabel
12426 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12427 tooltip : 'This field is required'
12438 // Roo.log(" no label && no align");
12445 ['xs','sm','md','lg'].map(function(size){
12446 if (settings[size]) {
12447 cfg.cls += ' col-' + size + '-' + settings[size];
12455 _initEventsCalled : false,
12458 initEvents: function()
12461 if (this._initEventsCalled) { // as we call render... prevent looping...
12464 this._initEventsCalled = true;
12467 throw "can not find store for combo";
12470 this.store = Roo.factory(this.store, Roo.data);
12472 // if we are building from html. then this element is so complex, that we can not really
12473 // use the rendered HTML.
12474 // so we have to trash and replace the previous code.
12475 if (Roo.XComponent.build_from_html) {
12477 // remove this element....
12478 var e = this.el.dom, k=0;
12479 while (e ) { e = e.previousSibling; ++k;}
12484 this.rendered = false;
12486 this.render(this.parent().getChildContainer(true), k);
12497 if(Roo.isTouch && this.mobileTouchView){
12498 this.initTouchView();
12503 this.initTickableEvents();
12507 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12509 if(this.hiddenName){
12511 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12513 this.hiddenField.dom.value =
12514 this.hiddenValue !== undefined ? this.hiddenValue :
12515 this.value !== undefined ? this.value : '';
12517 // prevent input submission
12518 this.el.dom.removeAttribute('name');
12519 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12524 // this.el.dom.setAttribute('autocomplete', 'off');
12527 var cls = 'x-combo-list';
12529 //this.list = new Roo.Layer({
12530 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12536 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12537 _this.list.setWidth(lw);
12540 this.list.on('mouseover', this.onViewOver, this);
12541 this.list.on('mousemove', this.onViewMove, this);
12543 this.list.on('scroll', this.onViewScroll, this);
12546 this.list.swallowEvent('mousewheel');
12547 this.assetHeight = 0;
12550 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12551 this.assetHeight += this.header.getHeight();
12554 this.innerList = this.list.createChild({cls:cls+'-inner'});
12555 this.innerList.on('mouseover', this.onViewOver, this);
12556 this.innerList.on('mousemove', this.onViewMove, this);
12557 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12559 if(this.allowBlank && !this.pageSize && !this.disableClear){
12560 this.footer = this.list.createChild({cls:cls+'-ft'});
12561 this.pageTb = new Roo.Toolbar(this.footer);
12565 this.footer = this.list.createChild({cls:cls+'-ft'});
12566 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12567 {pageSize: this.pageSize});
12571 if (this.pageTb && this.allowBlank && !this.disableClear) {
12573 this.pageTb.add(new Roo.Toolbar.Fill(), {
12574 cls: 'x-btn-icon x-btn-clear',
12576 handler: function()
12579 _this.clearValue();
12580 _this.onSelect(false, -1);
12585 this.assetHeight += this.footer.getHeight();
12590 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12593 this.view = new Roo.View(this.list, this.tpl, {
12594 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12596 //this.view.wrapEl.setDisplayed(false);
12597 this.view.on('click', this.onViewClick, this);
12601 this.store.on('beforeload', this.onBeforeLoad, this);
12602 this.store.on('load', this.onLoad, this);
12603 this.store.on('loadexception', this.onLoadException, this);
12605 if(this.resizable){
12606 this.resizer = new Roo.Resizable(this.list, {
12607 pinned:true, handles:'se'
12609 this.resizer.on('resize', function(r, w, h){
12610 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12611 this.listWidth = w;
12612 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12613 this.restrictHeight();
12615 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12618 if(!this.editable){
12619 this.editable = true;
12620 this.setEditable(false);
12625 if (typeof(this.events.add.listeners) != 'undefined') {
12627 this.addicon = this.wrap.createChild(
12628 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12630 this.addicon.on('click', function(e) {
12631 this.fireEvent('add', this);
12634 if (typeof(this.events.edit.listeners) != 'undefined') {
12636 this.editicon = this.wrap.createChild(
12637 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12638 if (this.addicon) {
12639 this.editicon.setStyle('margin-left', '40px');
12641 this.editicon.on('click', function(e) {
12643 // we fire even if inothing is selected..
12644 this.fireEvent('edit', this, this.lastData );
12650 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12651 "up" : function(e){
12652 this.inKeyMode = true;
12656 "down" : function(e){
12657 if(!this.isExpanded()){
12658 this.onTriggerClick();
12660 this.inKeyMode = true;
12665 "enter" : function(e){
12666 // this.onViewClick();
12670 if(this.fireEvent("specialkey", this, e)){
12671 this.onViewClick(false);
12677 "esc" : function(e){
12681 "tab" : function(e){
12684 if(this.fireEvent("specialkey", this, e)){
12685 this.onViewClick(false);
12693 doRelay : function(foo, bar, hname){
12694 if(hname == 'down' || this.scope.isExpanded()){
12695 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12704 this.queryDelay = Math.max(this.queryDelay || 10,
12705 this.mode == 'local' ? 10 : 250);
12708 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12710 if(this.typeAhead){
12711 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12713 if(this.editable !== false){
12714 this.inputEl().on("keyup", this.onKeyUp, this);
12716 if(this.forceSelection){
12717 this.inputEl().on('blur', this.doForce, this);
12721 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12722 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12726 initTickableEvents: function()
12730 if(this.hiddenName){
12732 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12734 this.hiddenField.dom.value =
12735 this.hiddenValue !== undefined ? this.hiddenValue :
12736 this.value !== undefined ? this.value : '';
12738 // prevent input submission
12739 this.el.dom.removeAttribute('name');
12740 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12745 // this.list = this.el.select('ul.dropdown-menu',true).first();
12747 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12748 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12749 if(this.triggerList){
12750 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12753 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12754 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12756 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12757 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12759 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12760 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12762 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12763 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12764 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12767 this.cancelBtn.hide();
12772 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12773 _this.list.setWidth(lw);
12776 this.list.on('mouseover', this.onViewOver, this);
12777 this.list.on('mousemove', this.onViewMove, this);
12779 this.list.on('scroll', this.onViewScroll, this);
12782 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>';
12785 this.view = new Roo.View(this.list, this.tpl, {
12786 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12789 //this.view.wrapEl.setDisplayed(false);
12790 this.view.on('click', this.onViewClick, this);
12794 this.store.on('beforeload', this.onBeforeLoad, this);
12795 this.store.on('load', this.onLoad, this);
12796 this.store.on('loadexception', this.onLoadException, this);
12799 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12800 "up" : function(e){
12801 this.inKeyMode = true;
12805 "down" : function(e){
12806 this.inKeyMode = true;
12810 "enter" : function(e){
12811 if(this.fireEvent("specialkey", this, e)){
12812 this.onViewClick(false);
12818 "esc" : function(e){
12819 this.onTickableFooterButtonClick(e, false, false);
12822 "tab" : function(e){
12823 this.fireEvent("specialkey", this, e);
12825 this.onTickableFooterButtonClick(e, false, false);
12832 doRelay : function(e, fn, key){
12833 if(this.scope.isExpanded()){
12834 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12843 this.queryDelay = Math.max(this.queryDelay || 10,
12844 this.mode == 'local' ? 10 : 250);
12847 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12849 if(this.typeAhead){
12850 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12853 if(this.editable !== false){
12854 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12859 onDestroy : function(){
12861 this.view.setStore(null);
12862 this.view.el.removeAllListeners();
12863 this.view.el.remove();
12864 this.view.purgeListeners();
12867 this.list.dom.innerHTML = '';
12871 this.store.un('beforeload', this.onBeforeLoad, this);
12872 this.store.un('load', this.onLoad, this);
12873 this.store.un('loadexception', this.onLoadException, this);
12875 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12879 fireKey : function(e){
12880 if(e.isNavKeyPress() && !this.list.isVisible()){
12881 this.fireEvent("specialkey", this, e);
12886 onResize: function(w, h){
12887 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12889 // if(typeof w != 'number'){
12890 // // we do not handle it!?!?
12893 // var tw = this.trigger.getWidth();
12894 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12895 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12897 // this.inputEl().setWidth( this.adjustWidth('input', x));
12899 // //this.trigger.setStyle('left', x+'px');
12901 // if(this.list && this.listWidth === undefined){
12902 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12903 // this.list.setWidth(lw);
12904 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12912 * Allow or prevent the user from directly editing the field text. If false is passed,
12913 * the user will only be able to select from the items defined in the dropdown list. This method
12914 * is the runtime equivalent of setting the 'editable' config option at config time.
12915 * @param {Boolean} value True to allow the user to directly edit the field text
12917 setEditable : function(value){
12918 if(value == this.editable){
12921 this.editable = value;
12923 this.inputEl().dom.setAttribute('readOnly', true);
12924 this.inputEl().on('mousedown', this.onTriggerClick, this);
12925 this.inputEl().addClass('x-combo-noedit');
12927 this.inputEl().dom.setAttribute('readOnly', false);
12928 this.inputEl().un('mousedown', this.onTriggerClick, this);
12929 this.inputEl().removeClass('x-combo-noedit');
12935 onBeforeLoad : function(combo,opts){
12936 if(!this.hasFocus){
12940 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12942 this.restrictHeight();
12943 this.selectedIndex = -1;
12947 onLoad : function(){
12949 this.hasQuery = false;
12951 if(!this.hasFocus){
12955 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12956 this.loading.hide();
12959 if(this.store.getCount() > 0){
12961 this.restrictHeight();
12962 if(this.lastQuery == this.allQuery){
12963 if(this.editable && !this.tickable){
12964 this.inputEl().dom.select();
12968 !this.selectByValue(this.value, true) &&
12971 !this.store.lastOptions ||
12972 typeof(this.store.lastOptions.add) == 'undefined' ||
12973 this.store.lastOptions.add != true
12976 this.select(0, true);
12979 if(this.autoFocus){
12982 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12983 this.taTask.delay(this.typeAheadDelay);
12987 this.onEmptyResults();
12993 onLoadException : function()
12995 this.hasQuery = false;
12997 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12998 this.loading.hide();
13001 if(this.tickable && this.editable){
13006 // only causes errors at present
13007 //Roo.log(this.store.reader.jsonData);
13008 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13010 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13016 onTypeAhead : function(){
13017 if(this.store.getCount() > 0){
13018 var r = this.store.getAt(0);
13019 var newValue = r.data[this.displayField];
13020 var len = newValue.length;
13021 var selStart = this.getRawValue().length;
13023 if(selStart != len){
13024 this.setRawValue(newValue);
13025 this.selectText(selStart, newValue.length);
13031 onSelect : function(record, index){
13033 if(this.fireEvent('beforeselect', this, record, index) !== false){
13035 this.setFromData(index > -1 ? record.data : false);
13038 this.fireEvent('select', this, record, index);
13043 * Returns the currently selected field value or empty string if no value is set.
13044 * @return {String} value The selected value
13046 getValue : function(){
13049 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13052 if(this.valueField){
13053 return typeof this.value != 'undefined' ? this.value : '';
13055 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13060 * Clears any text/value currently set in the field
13062 clearValue : function(){
13063 if(this.hiddenField){
13064 this.hiddenField.dom.value = '';
13067 this.setRawValue('');
13068 this.lastSelectionText = '';
13069 this.lastData = false;
13071 var close = this.closeTriggerEl();
13082 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13083 * will be displayed in the field. If the value does not match the data value of an existing item,
13084 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13085 * Otherwise the field will be blank (although the value will still be set).
13086 * @param {String} value The value to match
13088 setValue : function(v){
13095 if(this.valueField){
13096 var r = this.findRecord(this.valueField, v);
13098 text = r.data[this.displayField];
13099 }else if(this.valueNotFoundText !== undefined){
13100 text = this.valueNotFoundText;
13103 this.lastSelectionText = text;
13104 if(this.hiddenField){
13105 this.hiddenField.dom.value = v;
13107 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13110 var close = this.closeTriggerEl();
13113 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13119 * @property {Object} the last set data for the element
13124 * Sets the value of the field based on a object which is related to the record format for the store.
13125 * @param {Object} value the value to set as. or false on reset?
13127 setFromData : function(o){
13134 var dv = ''; // display value
13135 var vv = ''; // value value..
13137 if (this.displayField) {
13138 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13140 // this is an error condition!!!
13141 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13144 if(this.valueField){
13145 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13148 var close = this.closeTriggerEl();
13151 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13154 if(this.hiddenField){
13155 this.hiddenField.dom.value = vv;
13157 this.lastSelectionText = dv;
13158 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13162 // no hidden field.. - we store the value in 'value', but still display
13163 // display field!!!!
13164 this.lastSelectionText = dv;
13165 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13172 reset : function(){
13173 // overridden so that last data is reset..
13180 this.setValue(this.originalValue);
13181 //this.clearInvalid();
13182 this.lastData = false;
13184 this.view.clearSelections();
13190 findRecord : function(prop, value){
13192 if(this.store.getCount() > 0){
13193 this.store.each(function(r){
13194 if(r.data[prop] == value){
13204 getName: function()
13206 // returns hidden if it's set..
13207 if (!this.rendered) {return ''};
13208 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13212 onViewMove : function(e, t){
13213 this.inKeyMode = false;
13217 onViewOver : function(e, t){
13218 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13221 var item = this.view.findItemFromChild(t);
13224 var index = this.view.indexOf(item);
13225 this.select(index, false);
13230 onViewClick : function(view, doFocus, el, e)
13232 var index = this.view.getSelectedIndexes()[0];
13234 var r = this.store.getAt(index);
13238 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13245 Roo.each(this.tickItems, function(v,k){
13247 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13249 _this.tickItems.splice(k, 1);
13251 if(typeof(e) == 'undefined' && view == false){
13252 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13264 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13265 this.tickItems.push(r.data);
13268 if(typeof(e) == 'undefined' && view == false){
13269 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13276 this.onSelect(r, index);
13278 if(doFocus !== false && !this.blockFocus){
13279 this.inputEl().focus();
13284 restrictHeight : function(){
13285 //this.innerList.dom.style.height = '';
13286 //var inner = this.innerList.dom;
13287 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13288 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13289 //this.list.beginUpdate();
13290 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13291 this.list.alignTo(this.inputEl(), this.listAlign);
13292 this.list.alignTo(this.inputEl(), this.listAlign);
13293 //this.list.endUpdate();
13297 onEmptyResults : function(){
13299 if(this.tickable && this.editable){
13300 this.restrictHeight();
13308 * Returns true if the dropdown list is expanded, else false.
13310 isExpanded : function(){
13311 return this.list.isVisible();
13315 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13316 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13317 * @param {String} value The data value of the item to select
13318 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13319 * selected item if it is not currently in view (defaults to true)
13320 * @return {Boolean} True if the value matched an item in the list, else false
13322 selectByValue : function(v, scrollIntoView){
13323 if(v !== undefined && v !== null){
13324 var r = this.findRecord(this.valueField || this.displayField, v);
13326 this.select(this.store.indexOf(r), scrollIntoView);
13334 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13335 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13336 * @param {Number} index The zero-based index of the list item to select
13337 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13338 * selected item if it is not currently in view (defaults to true)
13340 select : function(index, scrollIntoView){
13341 this.selectedIndex = index;
13342 this.view.select(index);
13343 if(scrollIntoView !== false){
13344 var el = this.view.getNode(index);
13346 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13349 this.list.scrollChildIntoView(el, false);
13355 selectNext : function(){
13356 var ct = this.store.getCount();
13358 if(this.selectedIndex == -1){
13360 }else if(this.selectedIndex < ct-1){
13361 this.select(this.selectedIndex+1);
13367 selectPrev : function(){
13368 var ct = this.store.getCount();
13370 if(this.selectedIndex == -1){
13372 }else if(this.selectedIndex != 0){
13373 this.select(this.selectedIndex-1);
13379 onKeyUp : function(e){
13380 if(this.editable !== false && !e.isSpecialKey()){
13381 this.lastKey = e.getKey();
13382 this.dqTask.delay(this.queryDelay);
13387 validateBlur : function(){
13388 return !this.list || !this.list.isVisible();
13392 initQuery : function(){
13394 var v = this.getRawValue();
13396 if(this.tickable && this.editable){
13397 v = this.tickableInputEl().getValue();
13404 doForce : function(){
13405 if(this.inputEl().dom.value.length > 0){
13406 this.inputEl().dom.value =
13407 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13413 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13414 * query allowing the query action to be canceled if needed.
13415 * @param {String} query The SQL query to execute
13416 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13417 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13418 * saved in the current store (defaults to false)
13420 doQuery : function(q, forceAll){
13422 if(q === undefined || q === null){
13427 forceAll: forceAll,
13431 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13436 forceAll = qe.forceAll;
13437 if(forceAll === true || (q.length >= this.minChars)){
13439 this.hasQuery = true;
13441 if(this.lastQuery != q || this.alwaysQuery){
13442 this.lastQuery = q;
13443 if(this.mode == 'local'){
13444 this.selectedIndex = -1;
13446 this.store.clearFilter();
13449 if(this.specialFilter){
13450 this.fireEvent('specialfilter', this);
13455 this.store.filter(this.displayField, q);
13458 this.store.fireEvent("datachanged", this.store);
13465 this.store.baseParams[this.queryParam] = q;
13467 var options = {params : this.getParams(q)};
13470 options.add = true;
13471 options.params.start = this.page * this.pageSize;
13474 this.store.load(options);
13477 * this code will make the page width larger, at the beginning, the list not align correctly,
13478 * we should expand the list on onLoad
13479 * so command out it
13484 this.selectedIndex = -1;
13489 this.loadNext = false;
13493 getParams : function(q){
13495 //p[this.queryParam] = q;
13499 p.limit = this.pageSize;
13505 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13507 collapse : function(){
13508 if(!this.isExpanded()){
13515 this.hasFocus = false;
13517 this.cancelBtn.hide();
13518 this.trigger.show();
13521 this.tickableInputEl().dom.value = '';
13522 this.tickableInputEl().blur();
13527 Roo.get(document).un('mousedown', this.collapseIf, this);
13528 Roo.get(document).un('mousewheel', this.collapseIf, this);
13529 if (!this.editable) {
13530 Roo.get(document).un('keydown', this.listKeyPress, this);
13532 this.fireEvent('collapse', this);
13538 collapseIf : function(e){
13539 var in_combo = e.within(this.el);
13540 var in_list = e.within(this.list);
13541 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13543 if (in_combo || in_list || is_list) {
13544 //e.stopPropagation();
13549 this.onTickableFooterButtonClick(e, false, false);
13557 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13559 expand : function(){
13561 if(this.isExpanded() || !this.hasFocus){
13565 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13566 this.list.setWidth(lw);
13573 this.restrictHeight();
13577 this.tickItems = Roo.apply([], this.item);
13580 this.cancelBtn.show();
13581 this.trigger.hide();
13584 this.tickableInputEl().focus();
13589 Roo.get(document).on('mousedown', this.collapseIf, this);
13590 Roo.get(document).on('mousewheel', this.collapseIf, this);
13591 if (!this.editable) {
13592 Roo.get(document).on('keydown', this.listKeyPress, this);
13595 this.fireEvent('expand', this);
13599 // Implements the default empty TriggerField.onTriggerClick function
13600 onTriggerClick : function(e)
13602 Roo.log('trigger click');
13604 if(this.disabled || !this.triggerList){
13609 this.loadNext = false;
13611 if(this.isExpanded()){
13613 if (!this.blockFocus) {
13614 this.inputEl().focus();
13618 this.hasFocus = true;
13619 if(this.triggerAction == 'all') {
13620 this.doQuery(this.allQuery, true);
13622 this.doQuery(this.getRawValue());
13624 if (!this.blockFocus) {
13625 this.inputEl().focus();
13630 onTickableTriggerClick : function(e)
13637 this.loadNext = false;
13638 this.hasFocus = true;
13640 if(this.triggerAction == 'all') {
13641 this.doQuery(this.allQuery, true);
13643 this.doQuery(this.getRawValue());
13647 onSearchFieldClick : function(e)
13649 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13650 this.onTickableFooterButtonClick(e, false, false);
13654 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13659 this.loadNext = false;
13660 this.hasFocus = true;
13662 if(this.triggerAction == 'all') {
13663 this.doQuery(this.allQuery, true);
13665 this.doQuery(this.getRawValue());
13669 listKeyPress : function(e)
13671 //Roo.log('listkeypress');
13672 // scroll to first matching element based on key pres..
13673 if (e.isSpecialKey()) {
13676 var k = String.fromCharCode(e.getKey()).toUpperCase();
13679 var csel = this.view.getSelectedNodes();
13680 var cselitem = false;
13682 var ix = this.view.indexOf(csel[0]);
13683 cselitem = this.store.getAt(ix);
13684 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13690 this.store.each(function(v) {
13692 // start at existing selection.
13693 if (cselitem.id == v.id) {
13699 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13700 match = this.store.indexOf(v);
13706 if (match === false) {
13707 return true; // no more action?
13710 this.view.select(match);
13711 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13712 sn.scrollIntoView(sn.dom.parentNode, false);
13715 onViewScroll : function(e, t){
13717 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){
13721 this.hasQuery = true;
13723 this.loading = this.list.select('.loading', true).first();
13725 if(this.loading === null){
13726 this.list.createChild({
13728 cls: 'loading roo-select2-more-results roo-select2-active',
13729 html: 'Loading more results...'
13732 this.loading = this.list.select('.loading', true).first();
13734 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13736 this.loading.hide();
13739 this.loading.show();
13744 this.loadNext = true;
13746 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13751 addItem : function(o)
13753 var dv = ''; // display value
13755 if (this.displayField) {
13756 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13758 // this is an error condition!!!
13759 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13766 var choice = this.choices.createChild({
13768 cls: 'roo-select2-search-choice',
13777 cls: 'roo-select2-search-choice-close',
13782 }, this.searchField);
13784 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13786 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13794 this.inputEl().dom.value = '';
13799 onRemoveItem : function(e, _self, o)
13801 e.preventDefault();
13803 this.lastItem = Roo.apply([], this.item);
13805 var index = this.item.indexOf(o.data) * 1;
13808 Roo.log('not this item?!');
13812 this.item.splice(index, 1);
13817 this.fireEvent('remove', this, e);
13823 syncValue : function()
13825 if(!this.item.length){
13832 Roo.each(this.item, function(i){
13833 if(_this.valueField){
13834 value.push(i[_this.valueField]);
13841 this.value = value.join(',');
13843 if(this.hiddenField){
13844 this.hiddenField.dom.value = this.value;
13847 this.store.fireEvent("datachanged", this.store);
13852 clearItem : function()
13854 if(!this.multiple){
13860 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13868 if(this.tickable && !Roo.isTouch){
13869 this.view.refresh();
13873 inputEl: function ()
13875 if(Roo.isTouch && this.mobileTouchView){
13876 return this.el.select('input.form-control',true).first();
13880 return this.searchField;
13883 return this.el.select('input.form-control',true).first();
13887 onTickableFooterButtonClick : function(e, btn, el)
13889 e.preventDefault();
13891 this.lastItem = Roo.apply([], this.item);
13893 if(btn && btn.name == 'cancel'){
13894 this.tickItems = Roo.apply([], this.item);
13903 Roo.each(this.tickItems, function(o){
13911 validate : function()
13913 var v = this.getRawValue();
13916 v = this.getValue();
13919 if(this.disabled || this.allowBlank || v.length){
13924 this.markInvalid();
13928 tickableInputEl : function()
13930 if(!this.tickable || !this.editable){
13931 return this.inputEl();
13934 return this.inputEl().select('.roo-select2-search-field-input', true).first();
13938 getAutoCreateTouchView : function()
13943 cls: 'form-group' //input-group
13949 type : this.inputType,
13950 cls : 'form-control x-combo-noedit',
13951 autocomplete: 'new-password',
13952 placeholder : this.placeholder || '',
13957 input.name = this.name;
13961 input.cls += ' input-' + this.size;
13964 if (this.disabled) {
13965 input.disabled = true;
13976 inputblock.cls += ' input-group';
13978 inputblock.cn.unshift({
13980 cls : 'input-group-addon',
13985 if(this.removable && !this.multiple){
13986 inputblock.cls += ' roo-removable';
13988 inputblock.cn.push({
13991 cls : 'roo-combo-removable-btn close'
13995 if(this.hasFeedback && !this.allowBlank){
13997 inputblock.cls += ' has-feedback';
13999 inputblock.cn.push({
14001 cls: 'glyphicon form-control-feedback'
14008 inputblock.cls += (this.before) ? '' : ' input-group';
14010 inputblock.cn.push({
14012 cls : 'input-group-addon',
14023 cls: 'form-hidden-field'
14037 cls: 'form-hidden-field'
14041 cls: 'roo-select2-choices',
14045 cls: 'roo-select2-search-field',
14058 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14064 if(!this.multiple && this.showToggleBtn){
14071 if (this.caret != false) {
14074 cls: 'fa fa-' + this.caret
14081 cls : 'input-group-addon btn dropdown-toggle',
14086 cls: 'combobox-clear',
14100 combobox.cls += ' roo-select2-container-multi';
14103 var align = this.labelAlign || this.parentLabelAlign();
14107 if(this.fieldLabel.length && this.labelWidth){
14109 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14110 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14115 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14116 tooltip : 'This field is required'
14120 cls : 'control-label ' + lw,
14121 html : this.fieldLabel
14132 if(this.indicatorpos == 'right'){
14136 cls : 'control-label ' + lw,
14137 html : this.fieldLabel
14142 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14143 tooltip : 'This field is required'
14155 var settings = this;
14157 ['xs','sm','md','lg'].map(function(size){
14158 if (settings[size]) {
14159 cfg.cls += ' col-' + size + '-' + settings[size];
14166 initTouchView : function()
14168 this.renderTouchView();
14170 this.touchViewEl.on('scroll', function(){
14171 this.el.dom.scrollTop = 0;
14174 this.originalValue = this.getValue();
14176 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14178 this.inputEl().on("click", this.showTouchView, this);
14179 this.triggerEl.on("click", this.showTouchView, this);
14181 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14182 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14184 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14186 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14187 this.store.on('load', this.onTouchViewLoad, this);
14188 this.store.on('loadexception', this.onTouchViewLoadException, this);
14190 if(this.hiddenName){
14192 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14194 this.hiddenField.dom.value =
14195 this.hiddenValue !== undefined ? this.hiddenValue :
14196 this.value !== undefined ? this.value : '';
14198 this.el.dom.removeAttribute('name');
14199 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14203 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14204 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14207 if(this.removable && !this.multiple){
14208 var close = this.closeTriggerEl();
14210 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14211 close.on('click', this.removeBtnClick, this, close);
14215 * fix the bug in Safari iOS8
14217 this.inputEl().on("focus", function(e){
14218 document.activeElement.blur();
14226 renderTouchView : function()
14228 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14229 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14231 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14232 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14234 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14235 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14236 this.touchViewBodyEl.setStyle('overflow', 'auto');
14238 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14239 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14241 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14242 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14246 showTouchView : function()
14252 this.touchViewHeaderEl.hide();
14254 if(this.modalTitle.length){
14255 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14256 this.touchViewHeaderEl.show();
14259 this.touchViewEl.show();
14261 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14262 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14263 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14265 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14267 if(this.modalTitle.length){
14268 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14271 this.touchViewBodyEl.setHeight(bodyHeight);
14275 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14277 this.touchViewEl.addClass('in');
14280 this.doTouchViewQuery();
14284 hideTouchView : function()
14286 this.touchViewEl.removeClass('in');
14290 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14292 this.touchViewEl.setStyle('display', 'none');
14297 setTouchViewValue : function()
14304 Roo.each(this.tickItems, function(o){
14309 this.hideTouchView();
14312 doTouchViewQuery : function()
14321 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14325 if(!this.alwaysQuery || this.mode == 'local'){
14326 this.onTouchViewLoad();
14333 onTouchViewBeforeLoad : function(combo,opts)
14339 onTouchViewLoad : function()
14341 if(this.store.getCount() < 1){
14342 this.onTouchViewEmptyResults();
14346 this.clearTouchView();
14348 var rawValue = this.getRawValue();
14350 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14352 this.tickItems = [];
14354 this.store.data.each(function(d, rowIndex){
14355 var row = this.touchViewListGroup.createChild(template);
14357 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14358 row.addClass(d.data.cls);
14361 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14364 html : d.data[this.displayField]
14367 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14368 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14371 row.removeClass('selected');
14372 if(!this.multiple && this.valueField &&
14373 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14376 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14377 row.addClass('selected');
14380 if(this.multiple && this.valueField &&
14381 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14385 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14386 this.tickItems.push(d.data);
14389 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14393 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14395 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14397 if(this.modalTitle.length){
14398 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14401 var listHeight = this.touchViewListGroup.getHeight();
14405 if(firstChecked && listHeight > bodyHeight){
14406 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14411 onTouchViewLoadException : function()
14413 this.hideTouchView();
14416 onTouchViewEmptyResults : function()
14418 this.clearTouchView();
14420 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14422 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14426 clearTouchView : function()
14428 this.touchViewListGroup.dom.innerHTML = '';
14431 onTouchViewClick : function(e, el, o)
14433 e.preventDefault();
14436 var rowIndex = o.rowIndex;
14438 var r = this.store.getAt(rowIndex);
14440 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14442 if(!this.multiple){
14443 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14444 c.dom.removeAttribute('checked');
14447 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14449 this.setFromData(r.data);
14451 var close = this.closeTriggerEl();
14457 this.hideTouchView();
14459 this.fireEvent('select', this, r, rowIndex);
14464 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14465 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14466 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14470 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14471 this.addItem(r.data);
14472 this.tickItems.push(r.data);
14478 * @cfg {Boolean} grow
14482 * @cfg {Number} growMin
14486 * @cfg {Number} growMax
14495 Roo.apply(Roo.bootstrap.ComboBox, {
14499 cls: 'modal-header',
14521 cls: 'list-group-item',
14525 cls: 'roo-combobox-list-group-item-value'
14529 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14543 listItemCheckbox : {
14545 cls: 'list-group-item',
14549 cls: 'roo-combobox-list-group-item-value'
14553 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14569 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14574 cls: 'modal-footer',
14582 cls: 'col-xs-6 text-left',
14585 cls: 'btn btn-danger roo-touch-view-cancel',
14591 cls: 'col-xs-6 text-right',
14594 cls: 'btn btn-success roo-touch-view-ok',
14605 Roo.apply(Roo.bootstrap.ComboBox, {
14607 touchViewTemplate : {
14609 cls: 'modal fade roo-combobox-touch-view',
14613 cls: 'modal-dialog',
14614 style : 'position:fixed', // we have to fix position....
14618 cls: 'modal-content',
14620 Roo.bootstrap.ComboBox.header,
14621 Roo.bootstrap.ComboBox.body,
14622 Roo.bootstrap.ComboBox.footer
14631 * Ext JS Library 1.1.1
14632 * Copyright(c) 2006-2007, Ext JS, LLC.
14634 * Originally Released Under LGPL - original licence link has changed is not relivant.
14637 * <script type="text/javascript">
14642 * @extends Roo.util.Observable
14643 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14644 * This class also supports single and multi selection modes. <br>
14645 * Create a data model bound view:
14647 var store = new Roo.data.Store(...);
14649 var view = new Roo.View({
14651 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14653 singleSelect: true,
14654 selectedClass: "ydataview-selected",
14658 // listen for node click?
14659 view.on("click", function(vw, index, node, e){
14660 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14664 dataModel.load("foobar.xml");
14666 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14668 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14669 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14671 * Note: old style constructor is still suported (container, template, config)
14674 * Create a new View
14675 * @param {Object} config The config object
14678 Roo.View = function(config, depreciated_tpl, depreciated_config){
14680 this.parent = false;
14682 if (typeof(depreciated_tpl) == 'undefined') {
14683 // new way.. - universal constructor.
14684 Roo.apply(this, config);
14685 this.el = Roo.get(this.el);
14688 this.el = Roo.get(config);
14689 this.tpl = depreciated_tpl;
14690 Roo.apply(this, depreciated_config);
14692 this.wrapEl = this.el.wrap().wrap();
14693 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14696 if(typeof(this.tpl) == "string"){
14697 this.tpl = new Roo.Template(this.tpl);
14699 // support xtype ctors..
14700 this.tpl = new Roo.factory(this.tpl, Roo);
14704 this.tpl.compile();
14709 * @event beforeclick
14710 * Fires before a click is processed. Returns false to cancel the default action.
14711 * @param {Roo.View} this
14712 * @param {Number} index The index of the target node
14713 * @param {HTMLElement} node The target node
14714 * @param {Roo.EventObject} e The raw event object
14716 "beforeclick" : true,
14719 * Fires when a template node is clicked.
14720 * @param {Roo.View} this
14721 * @param {Number} index The index of the target node
14722 * @param {HTMLElement} node The target node
14723 * @param {Roo.EventObject} e The raw event object
14728 * Fires when a template node is double clicked.
14729 * @param {Roo.View} this
14730 * @param {Number} index The index of the target node
14731 * @param {HTMLElement} node The target node
14732 * @param {Roo.EventObject} e The raw event object
14736 * @event contextmenu
14737 * Fires when a template node is right clicked.
14738 * @param {Roo.View} this
14739 * @param {Number} index The index of the target node
14740 * @param {HTMLElement} node The target node
14741 * @param {Roo.EventObject} e The raw event object
14743 "contextmenu" : true,
14745 * @event selectionchange
14746 * Fires when the selected nodes change.
14747 * @param {Roo.View} this
14748 * @param {Array} selections Array of the selected nodes
14750 "selectionchange" : true,
14753 * @event beforeselect
14754 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14755 * @param {Roo.View} this
14756 * @param {HTMLElement} node The node to be selected
14757 * @param {Array} selections Array of currently selected nodes
14759 "beforeselect" : true,
14761 * @event preparedata
14762 * Fires on every row to render, to allow you to change the data.
14763 * @param {Roo.View} this
14764 * @param {Object} data to be rendered (change this)
14766 "preparedata" : true
14774 "click": this.onClick,
14775 "dblclick": this.onDblClick,
14776 "contextmenu": this.onContextMenu,
14780 this.selections = [];
14782 this.cmp = new Roo.CompositeElementLite([]);
14784 this.store = Roo.factory(this.store, Roo.data);
14785 this.setStore(this.store, true);
14788 if ( this.footer && this.footer.xtype) {
14790 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14792 this.footer.dataSource = this.store;
14793 this.footer.container = fctr;
14794 this.footer = Roo.factory(this.footer, Roo);
14795 fctr.insertFirst(this.el);
14797 // this is a bit insane - as the paging toolbar seems to detach the el..
14798 // dom.parentNode.parentNode.parentNode
14799 // they get detached?
14803 Roo.View.superclass.constructor.call(this);
14808 Roo.extend(Roo.View, Roo.util.Observable, {
14811 * @cfg {Roo.data.Store} store Data store to load data from.
14816 * @cfg {String|Roo.Element} el The container element.
14821 * @cfg {String|Roo.Template} tpl The template used by this View
14825 * @cfg {String} dataName the named area of the template to use as the data area
14826 * Works with domtemplates roo-name="name"
14830 * @cfg {String} selectedClass The css class to add to selected nodes
14832 selectedClass : "x-view-selected",
14834 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14839 * @cfg {String} text to display on mask (default Loading)
14843 * @cfg {Boolean} multiSelect Allow multiple selection
14845 multiSelect : false,
14847 * @cfg {Boolean} singleSelect Allow single selection
14849 singleSelect: false,
14852 * @cfg {Boolean} toggleSelect - selecting
14854 toggleSelect : false,
14857 * @cfg {Boolean} tickable - selecting
14862 * Returns the element this view is bound to.
14863 * @return {Roo.Element}
14865 getEl : function(){
14866 return this.wrapEl;
14872 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14874 refresh : function(){
14875 //Roo.log('refresh');
14878 // if we are using something like 'domtemplate', then
14879 // the what gets used is:
14880 // t.applySubtemplate(NAME, data, wrapping data..)
14881 // the outer template then get' applied with
14882 // the store 'extra data'
14883 // and the body get's added to the
14884 // roo-name="data" node?
14885 // <span class='roo-tpl-{name}'></span> ?????
14889 this.clearSelections();
14890 this.el.update("");
14892 var records = this.store.getRange();
14893 if(records.length < 1) {
14895 // is this valid?? = should it render a template??
14897 this.el.update(this.emptyText);
14901 if (this.dataName) {
14902 this.el.update(t.apply(this.store.meta)); //????
14903 el = this.el.child('.roo-tpl-' + this.dataName);
14906 for(var i = 0, len = records.length; i < len; i++){
14907 var data = this.prepareData(records[i].data, i, records[i]);
14908 this.fireEvent("preparedata", this, data, i, records[i]);
14910 var d = Roo.apply({}, data);
14913 Roo.apply(d, {'roo-id' : Roo.id()});
14917 Roo.each(this.parent.item, function(item){
14918 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14921 Roo.apply(d, {'roo-data-checked' : 'checked'});
14925 html[html.length] = Roo.util.Format.trim(
14927 t.applySubtemplate(this.dataName, d, this.store.meta) :
14934 el.update(html.join(""));
14935 this.nodes = el.dom.childNodes;
14936 this.updateIndexes(0);
14941 * Function to override to reformat the data that is sent to
14942 * the template for each node.
14943 * DEPRICATED - use the preparedata event handler.
14944 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14945 * a JSON object for an UpdateManager bound view).
14947 prepareData : function(data, index, record)
14949 this.fireEvent("preparedata", this, data, index, record);
14953 onUpdate : function(ds, record){
14954 // Roo.log('on update');
14955 this.clearSelections();
14956 var index = this.store.indexOf(record);
14957 var n = this.nodes[index];
14958 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14959 n.parentNode.removeChild(n);
14960 this.updateIndexes(index, index);
14966 onAdd : function(ds, records, index)
14968 //Roo.log(['on Add', ds, records, index] );
14969 this.clearSelections();
14970 if(this.nodes.length == 0){
14974 var n = this.nodes[index];
14975 for(var i = 0, len = records.length; i < len; i++){
14976 var d = this.prepareData(records[i].data, i, records[i]);
14978 this.tpl.insertBefore(n, d);
14981 this.tpl.append(this.el, d);
14984 this.updateIndexes(index);
14987 onRemove : function(ds, record, index){
14988 // Roo.log('onRemove');
14989 this.clearSelections();
14990 var el = this.dataName ?
14991 this.el.child('.roo-tpl-' + this.dataName) :
14994 el.dom.removeChild(this.nodes[index]);
14995 this.updateIndexes(index);
14999 * Refresh an individual node.
15000 * @param {Number} index
15002 refreshNode : function(index){
15003 this.onUpdate(this.store, this.store.getAt(index));
15006 updateIndexes : function(startIndex, endIndex){
15007 var ns = this.nodes;
15008 startIndex = startIndex || 0;
15009 endIndex = endIndex || ns.length - 1;
15010 for(var i = startIndex; i <= endIndex; i++){
15011 ns[i].nodeIndex = i;
15016 * Changes the data store this view uses and refresh the view.
15017 * @param {Store} store
15019 setStore : function(store, initial){
15020 if(!initial && this.store){
15021 this.store.un("datachanged", this.refresh);
15022 this.store.un("add", this.onAdd);
15023 this.store.un("remove", this.onRemove);
15024 this.store.un("update", this.onUpdate);
15025 this.store.un("clear", this.refresh);
15026 this.store.un("beforeload", this.onBeforeLoad);
15027 this.store.un("load", this.onLoad);
15028 this.store.un("loadexception", this.onLoad);
15032 store.on("datachanged", this.refresh, this);
15033 store.on("add", this.onAdd, this);
15034 store.on("remove", this.onRemove, this);
15035 store.on("update", this.onUpdate, this);
15036 store.on("clear", this.refresh, this);
15037 store.on("beforeload", this.onBeforeLoad, this);
15038 store.on("load", this.onLoad, this);
15039 store.on("loadexception", this.onLoad, this);
15047 * onbeforeLoad - masks the loading area.
15050 onBeforeLoad : function(store,opts)
15052 //Roo.log('onBeforeLoad');
15054 this.el.update("");
15056 this.el.mask(this.mask ? this.mask : "Loading" );
15058 onLoad : function ()
15065 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15066 * @param {HTMLElement} node
15067 * @return {HTMLElement} The template node
15069 findItemFromChild : function(node){
15070 var el = this.dataName ?
15071 this.el.child('.roo-tpl-' + this.dataName,true) :
15074 if(!node || node.parentNode == el){
15077 var p = node.parentNode;
15078 while(p && p != el){
15079 if(p.parentNode == el){
15088 onClick : function(e){
15089 var item = this.findItemFromChild(e.getTarget());
15091 var index = this.indexOf(item);
15092 if(this.onItemClick(item, index, e) !== false){
15093 this.fireEvent("click", this, index, item, e);
15096 this.clearSelections();
15101 onContextMenu : function(e){
15102 var item = this.findItemFromChild(e.getTarget());
15104 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15109 onDblClick : function(e){
15110 var item = this.findItemFromChild(e.getTarget());
15112 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15116 onItemClick : function(item, index, e)
15118 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15121 if (this.toggleSelect) {
15122 var m = this.isSelected(item) ? 'unselect' : 'select';
15125 _t[m](item, true, false);
15128 if(this.multiSelect || this.singleSelect){
15129 if(this.multiSelect && e.shiftKey && this.lastSelection){
15130 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15132 this.select(item, this.multiSelect && e.ctrlKey);
15133 this.lastSelection = item;
15136 if(!this.tickable){
15137 e.preventDefault();
15145 * Get the number of selected nodes.
15148 getSelectionCount : function(){
15149 return this.selections.length;
15153 * Get the currently selected nodes.
15154 * @return {Array} An array of HTMLElements
15156 getSelectedNodes : function(){
15157 return this.selections;
15161 * Get the indexes of the selected nodes.
15164 getSelectedIndexes : function(){
15165 var indexes = [], s = this.selections;
15166 for(var i = 0, len = s.length; i < len; i++){
15167 indexes.push(s[i].nodeIndex);
15173 * Clear all selections
15174 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15176 clearSelections : function(suppressEvent){
15177 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15178 this.cmp.elements = this.selections;
15179 this.cmp.removeClass(this.selectedClass);
15180 this.selections = [];
15181 if(!suppressEvent){
15182 this.fireEvent("selectionchange", this, this.selections);
15188 * Returns true if the passed node is selected
15189 * @param {HTMLElement/Number} node The node or node index
15190 * @return {Boolean}
15192 isSelected : function(node){
15193 var s = this.selections;
15197 node = this.getNode(node);
15198 return s.indexOf(node) !== -1;
15203 * @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
15204 * @param {Boolean} keepExisting (optional) true to keep existing selections
15205 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15207 select : function(nodeInfo, keepExisting, suppressEvent){
15208 if(nodeInfo instanceof Array){
15210 this.clearSelections(true);
15212 for(var i = 0, len = nodeInfo.length; i < len; i++){
15213 this.select(nodeInfo[i], true, true);
15217 var node = this.getNode(nodeInfo);
15218 if(!node || this.isSelected(node)){
15219 return; // already selected.
15222 this.clearSelections(true);
15225 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15226 Roo.fly(node).addClass(this.selectedClass);
15227 this.selections.push(node);
15228 if(!suppressEvent){
15229 this.fireEvent("selectionchange", this, this.selections);
15237 * @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
15238 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15239 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15241 unselect : function(nodeInfo, keepExisting, suppressEvent)
15243 if(nodeInfo instanceof Array){
15244 Roo.each(this.selections, function(s) {
15245 this.unselect(s, nodeInfo);
15249 var node = this.getNode(nodeInfo);
15250 if(!node || !this.isSelected(node)){
15251 //Roo.log("not selected");
15252 return; // not selected.
15256 Roo.each(this.selections, function(s) {
15258 Roo.fly(node).removeClass(this.selectedClass);
15265 this.selections= ns;
15266 this.fireEvent("selectionchange", this, this.selections);
15270 * Gets a template node.
15271 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15272 * @return {HTMLElement} The node or null if it wasn't found
15274 getNode : function(nodeInfo){
15275 if(typeof nodeInfo == "string"){
15276 return document.getElementById(nodeInfo);
15277 }else if(typeof nodeInfo == "number"){
15278 return this.nodes[nodeInfo];
15284 * Gets a range template nodes.
15285 * @param {Number} startIndex
15286 * @param {Number} endIndex
15287 * @return {Array} An array of nodes
15289 getNodes : function(start, end){
15290 var ns = this.nodes;
15291 start = start || 0;
15292 end = typeof end == "undefined" ? ns.length - 1 : end;
15295 for(var i = start; i <= end; i++){
15299 for(var i = start; i >= end; i--){
15307 * Finds the index of the passed node
15308 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15309 * @return {Number} The index of the node or -1
15311 indexOf : function(node){
15312 node = this.getNode(node);
15313 if(typeof node.nodeIndex == "number"){
15314 return node.nodeIndex;
15316 var ns = this.nodes;
15317 for(var i = 0, len = ns.length; i < len; i++){
15328 * based on jquery fullcalendar
15332 Roo.bootstrap = Roo.bootstrap || {};
15334 * @class Roo.bootstrap.Calendar
15335 * @extends Roo.bootstrap.Component
15336 * Bootstrap Calendar class
15337 * @cfg {Boolean} loadMask (true|false) default false
15338 * @cfg {Object} header generate the user specific header of the calendar, default false
15341 * Create a new Container
15342 * @param {Object} config The config object
15347 Roo.bootstrap.Calendar = function(config){
15348 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15352 * Fires when a date is selected
15353 * @param {DatePicker} this
15354 * @param {Date} date The selected date
15358 * @event monthchange
15359 * Fires when the displayed month changes
15360 * @param {DatePicker} this
15361 * @param {Date} date The selected month
15363 'monthchange': true,
15365 * @event evententer
15366 * Fires when mouse over an event
15367 * @param {Calendar} this
15368 * @param {event} Event
15370 'evententer': true,
15372 * @event eventleave
15373 * Fires when the mouse leaves an
15374 * @param {Calendar} this
15377 'eventleave': true,
15379 * @event eventclick
15380 * Fires when the mouse click an
15381 * @param {Calendar} this
15390 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15393 * @cfg {Number} startDay
15394 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15402 getAutoCreate : function(){
15405 var fc_button = function(name, corner, style, content ) {
15406 return Roo.apply({},{
15408 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15410 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15413 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15424 style : 'width:100%',
15431 cls : 'fc-header-left',
15433 fc_button('prev', 'left', 'arrow', '‹' ),
15434 fc_button('next', 'right', 'arrow', '›' ),
15435 { tag: 'span', cls: 'fc-header-space' },
15436 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15444 cls : 'fc-header-center',
15448 cls: 'fc-header-title',
15451 html : 'month / year'
15459 cls : 'fc-header-right',
15461 /* fc_button('month', 'left', '', 'month' ),
15462 fc_button('week', '', '', 'week' ),
15463 fc_button('day', 'right', '', 'day' )
15475 header = this.header;
15478 var cal_heads = function() {
15480 // fixme - handle this.
15482 for (var i =0; i < Date.dayNames.length; i++) {
15483 var d = Date.dayNames[i];
15486 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15487 html : d.substring(0,3)
15491 ret[0].cls += ' fc-first';
15492 ret[6].cls += ' fc-last';
15495 var cal_cell = function(n) {
15498 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15503 cls: 'fc-day-number',
15507 cls: 'fc-day-content',
15511 style: 'position: relative;' // height: 17px;
15523 var cal_rows = function() {
15526 for (var r = 0; r < 6; r++) {
15533 for (var i =0; i < Date.dayNames.length; i++) {
15534 var d = Date.dayNames[i];
15535 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15538 row.cn[0].cls+=' fc-first';
15539 row.cn[0].cn[0].style = 'min-height:90px';
15540 row.cn[6].cls+=' fc-last';
15544 ret[0].cls += ' fc-first';
15545 ret[4].cls += ' fc-prev-last';
15546 ret[5].cls += ' fc-last';
15553 cls: 'fc-border-separate',
15554 style : 'width:100%',
15562 cls : 'fc-first fc-last',
15580 cls : 'fc-content',
15581 style : "position: relative;",
15584 cls : 'fc-view fc-view-month fc-grid',
15585 style : 'position: relative',
15586 unselectable : 'on',
15589 cls : 'fc-event-container',
15590 style : 'position:absolute;z-index:8;top:0;left:0;'
15608 initEvents : function()
15611 throw "can not find store for calendar";
15617 style: "text-align:center",
15621 style: "background-color:white;width:50%;margin:250 auto",
15625 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15636 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15638 var size = this.el.select('.fc-content', true).first().getSize();
15639 this.maskEl.setSize(size.width, size.height);
15640 this.maskEl.enableDisplayMode("block");
15641 if(!this.loadMask){
15642 this.maskEl.hide();
15645 this.store = Roo.factory(this.store, Roo.data);
15646 this.store.on('load', this.onLoad, this);
15647 this.store.on('beforeload', this.onBeforeLoad, this);
15651 this.cells = this.el.select('.fc-day',true);
15652 //Roo.log(this.cells);
15653 this.textNodes = this.el.query('.fc-day-number');
15654 this.cells.addClassOnOver('fc-state-hover');
15656 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15657 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15658 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15659 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15661 this.on('monthchange', this.onMonthChange, this);
15663 this.update(new Date().clearTime());
15666 resize : function() {
15667 var sz = this.el.getSize();
15669 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15670 this.el.select('.fc-day-content div',true).setHeight(34);
15675 showPrevMonth : function(e){
15676 this.update(this.activeDate.add("mo", -1));
15678 showToday : function(e){
15679 this.update(new Date().clearTime());
15682 showNextMonth : function(e){
15683 this.update(this.activeDate.add("mo", 1));
15687 showPrevYear : function(){
15688 this.update(this.activeDate.add("y", -1));
15692 showNextYear : function(){
15693 this.update(this.activeDate.add("y", 1));
15698 update : function(date)
15700 var vd = this.activeDate;
15701 this.activeDate = date;
15702 // if(vd && this.el){
15703 // var t = date.getTime();
15704 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15705 // Roo.log('using add remove');
15707 // this.fireEvent('monthchange', this, date);
15709 // this.cells.removeClass("fc-state-highlight");
15710 // this.cells.each(function(c){
15711 // if(c.dateValue == t){
15712 // c.addClass("fc-state-highlight");
15713 // setTimeout(function(){
15714 // try{c.dom.firstChild.focus();}catch(e){}
15724 var days = date.getDaysInMonth();
15726 var firstOfMonth = date.getFirstDateOfMonth();
15727 var startingPos = firstOfMonth.getDay()-this.startDay;
15729 if(startingPos < this.startDay){
15733 var pm = date.add(Date.MONTH, -1);
15734 var prevStart = pm.getDaysInMonth()-startingPos;
15736 this.cells = this.el.select('.fc-day',true);
15737 this.textNodes = this.el.query('.fc-day-number');
15738 this.cells.addClassOnOver('fc-state-hover');
15740 var cells = this.cells.elements;
15741 var textEls = this.textNodes;
15743 Roo.each(cells, function(cell){
15744 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15747 days += startingPos;
15749 // convert everything to numbers so it's fast
15750 var day = 86400000;
15751 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15754 //Roo.log(prevStart);
15756 var today = new Date().clearTime().getTime();
15757 var sel = date.clearTime().getTime();
15758 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15759 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15760 var ddMatch = this.disabledDatesRE;
15761 var ddText = this.disabledDatesText;
15762 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15763 var ddaysText = this.disabledDaysText;
15764 var format = this.format;
15766 var setCellClass = function(cal, cell){
15770 //Roo.log('set Cell Class');
15772 var t = d.getTime();
15776 cell.dateValue = t;
15778 cell.className += " fc-today";
15779 cell.className += " fc-state-highlight";
15780 cell.title = cal.todayText;
15783 // disable highlight in other month..
15784 //cell.className += " fc-state-highlight";
15789 cell.className = " fc-state-disabled";
15790 cell.title = cal.minText;
15794 cell.className = " fc-state-disabled";
15795 cell.title = cal.maxText;
15799 if(ddays.indexOf(d.getDay()) != -1){
15800 cell.title = ddaysText;
15801 cell.className = " fc-state-disabled";
15804 if(ddMatch && format){
15805 var fvalue = d.dateFormat(format);
15806 if(ddMatch.test(fvalue)){
15807 cell.title = ddText.replace("%0", fvalue);
15808 cell.className = " fc-state-disabled";
15812 if (!cell.initialClassName) {
15813 cell.initialClassName = cell.dom.className;
15816 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15821 for(; i < startingPos; i++) {
15822 textEls[i].innerHTML = (++prevStart);
15823 d.setDate(d.getDate()+1);
15825 cells[i].className = "fc-past fc-other-month";
15826 setCellClass(this, cells[i]);
15831 for(; i < days; i++){
15832 intDay = i - startingPos + 1;
15833 textEls[i].innerHTML = (intDay);
15834 d.setDate(d.getDate()+1);
15836 cells[i].className = ''; // "x-date-active";
15837 setCellClass(this, cells[i]);
15841 for(; i < 42; i++) {
15842 textEls[i].innerHTML = (++extraDays);
15843 d.setDate(d.getDate()+1);
15845 cells[i].className = "fc-future fc-other-month";
15846 setCellClass(this, cells[i]);
15849 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15851 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15853 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15854 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15856 if(totalRows != 6){
15857 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15858 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15861 this.fireEvent('monthchange', this, date);
15865 if(!this.internalRender){
15866 var main = this.el.dom.firstChild;
15867 var w = main.offsetWidth;
15868 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15869 Roo.fly(main).setWidth(w);
15870 this.internalRender = true;
15871 // opera does not respect the auto grow header center column
15872 // then, after it gets a width opera refuses to recalculate
15873 // without a second pass
15874 if(Roo.isOpera && !this.secondPass){
15875 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15876 this.secondPass = true;
15877 this.update.defer(10, this, [date]);
15884 findCell : function(dt) {
15885 dt = dt.clearTime().getTime();
15887 this.cells.each(function(c){
15888 //Roo.log("check " +c.dateValue + '?=' + dt);
15889 if(c.dateValue == dt){
15899 findCells : function(ev) {
15900 var s = ev.start.clone().clearTime().getTime();
15902 var e= ev.end.clone().clearTime().getTime();
15905 this.cells.each(function(c){
15906 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15908 if(c.dateValue > e){
15911 if(c.dateValue < s){
15920 // findBestRow: function(cells)
15924 // for (var i =0 ; i < cells.length;i++) {
15925 // ret = Math.max(cells[i].rows || 0,ret);
15932 addItem : function(ev)
15934 // look for vertical location slot in
15935 var cells = this.findCells(ev);
15937 // ev.row = this.findBestRow(cells);
15939 // work out the location.
15943 for(var i =0; i < cells.length; i++) {
15945 cells[i].row = cells[0].row;
15948 cells[i].row = cells[i].row + 1;
15958 if (crow.start.getY() == cells[i].getY()) {
15960 crow.end = cells[i];
15977 cells[0].events.push(ev);
15979 this.calevents.push(ev);
15982 clearEvents: function() {
15984 if(!this.calevents){
15988 Roo.each(this.cells.elements, function(c){
15994 Roo.each(this.calevents, function(e) {
15995 Roo.each(e.els, function(el) {
15996 el.un('mouseenter' ,this.onEventEnter, this);
15997 el.un('mouseleave' ,this.onEventLeave, this);
16002 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16008 renderEvents: function()
16012 this.cells.each(function(c) {
16021 if(c.row != c.events.length){
16022 r = 4 - (4 - (c.row - c.events.length));
16025 c.events = ev.slice(0, r);
16026 c.more = ev.slice(r);
16028 if(c.more.length && c.more.length == 1){
16029 c.events.push(c.more.pop());
16032 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16036 this.cells.each(function(c) {
16038 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16041 for (var e = 0; e < c.events.length; e++){
16042 var ev = c.events[e];
16043 var rows = ev.rows;
16045 for(var i = 0; i < rows.length; i++) {
16047 // how many rows should it span..
16050 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16051 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16053 unselectable : "on",
16056 cls: 'fc-event-inner',
16060 // cls: 'fc-event-time',
16061 // html : cells.length > 1 ? '' : ev.time
16065 cls: 'fc-event-title',
16066 html : String.format('{0}', ev.title)
16073 cls: 'ui-resizable-handle ui-resizable-e',
16074 html : '  '
16081 cfg.cls += ' fc-event-start';
16083 if ((i+1) == rows.length) {
16084 cfg.cls += ' fc-event-end';
16087 var ctr = _this.el.select('.fc-event-container',true).first();
16088 var cg = ctr.createChild(cfg);
16090 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16091 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16093 var r = (c.more.length) ? 1 : 0;
16094 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16095 cg.setWidth(ebox.right - sbox.x -2);
16097 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16098 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16099 cg.on('click', _this.onEventClick, _this, ev);
16110 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16111 style : 'position: absolute',
16112 unselectable : "on",
16115 cls: 'fc-event-inner',
16119 cls: 'fc-event-title',
16127 cls: 'ui-resizable-handle ui-resizable-e',
16128 html : '  '
16134 var ctr = _this.el.select('.fc-event-container',true).first();
16135 var cg = ctr.createChild(cfg);
16137 var sbox = c.select('.fc-day-content',true).first().getBox();
16138 var ebox = c.select('.fc-day-content',true).first().getBox();
16140 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16141 cg.setWidth(ebox.right - sbox.x -2);
16143 cg.on('click', _this.onMoreEventClick, _this, c.more);
16153 onEventEnter: function (e, el,event,d) {
16154 this.fireEvent('evententer', this, el, event);
16157 onEventLeave: function (e, el,event,d) {
16158 this.fireEvent('eventleave', this, el, event);
16161 onEventClick: function (e, el,event,d) {
16162 this.fireEvent('eventclick', this, el, event);
16165 onMonthChange: function () {
16169 onMoreEventClick: function(e, el, more)
16173 this.calpopover.placement = 'right';
16174 this.calpopover.setTitle('More');
16176 this.calpopover.setContent('');
16178 var ctr = this.calpopover.el.select('.popover-content', true).first();
16180 Roo.each(more, function(m){
16182 cls : 'fc-event-hori fc-event-draggable',
16185 var cg = ctr.createChild(cfg);
16187 cg.on('click', _this.onEventClick, _this, m);
16190 this.calpopover.show(el);
16195 onLoad: function ()
16197 this.calevents = [];
16200 if(this.store.getCount() > 0){
16201 this.store.data.each(function(d){
16204 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16205 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16206 time : d.data.start_time,
16207 title : d.data.title,
16208 description : d.data.description,
16209 venue : d.data.venue
16214 this.renderEvents();
16216 if(this.calevents.length && this.loadMask){
16217 this.maskEl.hide();
16221 onBeforeLoad: function()
16223 this.clearEvents();
16225 this.maskEl.show();
16239 * @class Roo.bootstrap.Popover
16240 * @extends Roo.bootstrap.Component
16241 * Bootstrap Popover class
16242 * @cfg {String} html contents of the popover (or false to use children..)
16243 * @cfg {String} title of popover (or false to hide)
16244 * @cfg {String} placement how it is placed
16245 * @cfg {String} trigger click || hover (or false to trigger manually)
16246 * @cfg {String} over what (parent or false to trigger manually.)
16247 * @cfg {Number} delay - delay before showing
16250 * Create a new Popover
16251 * @param {Object} config The config object
16254 Roo.bootstrap.Popover = function(config){
16255 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16261 * After the popover show
16263 * @param {Roo.bootstrap.Popover} this
16268 * After the popover hide
16270 * @param {Roo.bootstrap.Popover} this
16276 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16278 title: 'Fill in a title',
16281 placement : 'right',
16282 trigger : 'hover', // hover
16288 can_build_overlaid : false,
16290 getChildContainer : function()
16292 return this.el.select('.popover-content',true).first();
16295 getAutoCreate : function(){
16298 cls : 'popover roo-dynamic',
16299 style: 'display:block',
16305 cls : 'popover-inner',
16309 cls: 'popover-title',
16313 cls : 'popover-content',
16324 setTitle: function(str)
16327 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16329 setContent: function(str)
16332 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16334 // as it get's added to the bottom of the page.
16335 onRender : function(ct, position)
16337 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16339 var cfg = Roo.apply({}, this.getAutoCreate());
16343 cfg.cls += ' ' + this.cls;
16346 cfg.style = this.style;
16348 //Roo.log("adding to ");
16349 this.el = Roo.get(document.body).createChild(cfg, position);
16350 // Roo.log(this.el);
16355 initEvents : function()
16357 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16358 this.el.enableDisplayMode('block');
16360 if (this.over === false) {
16363 if (this.triggers === false) {
16366 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16367 var triggers = this.trigger ? this.trigger.split(' ') : [];
16368 Roo.each(triggers, function(trigger) {
16370 if (trigger == 'click') {
16371 on_el.on('click', this.toggle, this);
16372 } else if (trigger != 'manual') {
16373 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16374 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16376 on_el.on(eventIn ,this.enter, this);
16377 on_el.on(eventOut, this.leave, this);
16388 toggle : function () {
16389 this.hoverState == 'in' ? this.leave() : this.enter();
16392 enter : function () {
16394 clearTimeout(this.timeout);
16396 this.hoverState = 'in';
16398 if (!this.delay || !this.delay.show) {
16403 this.timeout = setTimeout(function () {
16404 if (_t.hoverState == 'in') {
16407 }, this.delay.show)
16410 leave : function() {
16411 clearTimeout(this.timeout);
16413 this.hoverState = 'out';
16415 if (!this.delay || !this.delay.hide) {
16420 this.timeout = setTimeout(function () {
16421 if (_t.hoverState == 'out') {
16424 }, this.delay.hide)
16427 show : function (on_el)
16430 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16434 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16435 if (this.html !== false) {
16436 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16438 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16439 if (!this.title.length) {
16440 this.el.select('.popover-title',true).hide();
16443 var placement = typeof this.placement == 'function' ?
16444 this.placement.call(this, this.el, on_el) :
16447 var autoToken = /\s?auto?\s?/i;
16448 var autoPlace = autoToken.test(placement);
16450 placement = placement.replace(autoToken, '') || 'top';
16454 //this.el.setXY([0,0]);
16456 this.el.dom.style.display='block';
16457 this.el.addClass(placement);
16459 //this.el.appendTo(on_el);
16461 var p = this.getPosition();
16462 var box = this.el.getBox();
16467 var align = Roo.bootstrap.Popover.alignment[placement];
16468 this.el.alignTo(on_el, align[0],align[1]);
16469 //var arrow = this.el.select('.arrow',true).first();
16470 //arrow.set(align[2],
16472 this.el.addClass('in');
16475 if (this.el.hasClass('fade')) {
16479 this.hoverState = 'in';
16481 this.fireEvent('show', this);
16486 this.el.setXY([0,0]);
16487 this.el.removeClass('in');
16489 this.hoverState = null;
16491 this.fireEvent('hide', this);
16496 Roo.bootstrap.Popover.alignment = {
16497 'left' : ['r-l', [-10,0], 'right'],
16498 'right' : ['l-r', [10,0], 'left'],
16499 'bottom' : ['t-b', [0,10], 'top'],
16500 'top' : [ 'b-t', [0,-10], 'bottom']
16511 * @class Roo.bootstrap.Progress
16512 * @extends Roo.bootstrap.Component
16513 * Bootstrap Progress class
16514 * @cfg {Boolean} striped striped of the progress bar
16515 * @cfg {Boolean} active animated of the progress bar
16519 * Create a new Progress
16520 * @param {Object} config The config object
16523 Roo.bootstrap.Progress = function(config){
16524 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16527 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16532 getAutoCreate : function(){
16540 cfg.cls += ' progress-striped';
16544 cfg.cls += ' active';
16563 * @class Roo.bootstrap.ProgressBar
16564 * @extends Roo.bootstrap.Component
16565 * Bootstrap ProgressBar class
16566 * @cfg {Number} aria_valuenow aria-value now
16567 * @cfg {Number} aria_valuemin aria-value min
16568 * @cfg {Number} aria_valuemax aria-value max
16569 * @cfg {String} label label for the progress bar
16570 * @cfg {String} panel (success | info | warning | danger )
16571 * @cfg {String} role role of the progress bar
16572 * @cfg {String} sr_only text
16576 * Create a new ProgressBar
16577 * @param {Object} config The config object
16580 Roo.bootstrap.ProgressBar = function(config){
16581 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16584 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16588 aria_valuemax : 100,
16594 getAutoCreate : function()
16599 cls: 'progress-bar',
16600 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16612 cfg.role = this.role;
16615 if(this.aria_valuenow){
16616 cfg['aria-valuenow'] = this.aria_valuenow;
16619 if(this.aria_valuemin){
16620 cfg['aria-valuemin'] = this.aria_valuemin;
16623 if(this.aria_valuemax){
16624 cfg['aria-valuemax'] = this.aria_valuemax;
16627 if(this.label && !this.sr_only){
16628 cfg.html = this.label;
16632 cfg.cls += ' progress-bar-' + this.panel;
16638 update : function(aria_valuenow)
16640 this.aria_valuenow = aria_valuenow;
16642 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16657 * @class Roo.bootstrap.TabGroup
16658 * @extends Roo.bootstrap.Column
16659 * Bootstrap Column class
16660 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16661 * @cfg {Boolean} carousel true to make the group behave like a carousel
16662 * @cfg {Boolean} bullets show bullets for the panels
16663 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16664 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16665 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16666 * @cfg {Boolean} showarrow (true|false) show arrow default true
16669 * Create a new TabGroup
16670 * @param {Object} config The config object
16673 Roo.bootstrap.TabGroup = function(config){
16674 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16676 this.navId = Roo.id();
16679 Roo.bootstrap.TabGroup.register(this);
16683 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16686 transition : false,
16691 slideOnTouch : false,
16694 getAutoCreate : function()
16696 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16698 cfg.cls += ' tab-content';
16700 if (this.carousel) {
16701 cfg.cls += ' carousel slide';
16704 cls : 'carousel-inner',
16708 if(this.bullets && !Roo.isTouch){
16711 cls : 'carousel-bullets',
16715 if(this.bullets_cls){
16716 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16723 cfg.cn[0].cn.push(bullets);
16726 if(this.showarrow){
16727 cfg.cn[0].cn.push({
16729 class : 'carousel-arrow',
16733 class : 'carousel-prev',
16737 class : 'fa fa-chevron-left'
16743 class : 'carousel-next',
16747 class : 'fa fa-chevron-right'
16760 initEvents: function()
16762 if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16763 this.el.on("touchstart", this.onTouchStart, this);
16766 if(this.autoslide){
16769 this.slideFn = window.setInterval(function() {
16770 _this.showPanelNext();
16774 if(this.showarrow){
16775 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16776 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16782 onTouchStart : function(e, el, o)
16784 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16788 this.showPanelNext();
16791 getChildContainer : function()
16793 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16797 * register a Navigation item
16798 * @param {Roo.bootstrap.NavItem} the navitem to add
16800 register : function(item)
16802 this.tabs.push( item);
16803 item.navId = this.navId; // not really needed..
16808 getActivePanel : function()
16811 Roo.each(this.tabs, function(t) {
16821 getPanelByName : function(n)
16824 Roo.each(this.tabs, function(t) {
16825 if (t.tabId == n) {
16833 indexOfPanel : function(p)
16836 Roo.each(this.tabs, function(t,i) {
16837 if (t.tabId == p.tabId) {
16846 * show a specific panel
16847 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16848 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16850 showPanel : function (pan)
16852 if(this.transition || typeof(pan) == 'undefined'){
16853 Roo.log("waiting for the transitionend");
16857 if (typeof(pan) == 'number') {
16858 pan = this.tabs[pan];
16861 if (typeof(pan) == 'string') {
16862 pan = this.getPanelByName(pan);
16865 var cur = this.getActivePanel();
16868 Roo.log('pan or acitve pan is undefined');
16872 if (pan.tabId == this.getActivePanel().tabId) {
16876 if (false === cur.fireEvent('beforedeactivate')) {
16880 if(this.bullets > 0 && !Roo.isTouch){
16881 this.setActiveBullet(this.indexOfPanel(pan));
16884 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16886 this.transition = true;
16887 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16888 var lr = dir == 'next' ? 'left' : 'right';
16889 pan.el.addClass(dir); // or prev
16890 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16891 cur.el.addClass(lr); // or right
16892 pan.el.addClass(lr);
16895 cur.el.on('transitionend', function() {
16896 Roo.log("trans end?");
16898 pan.el.removeClass([lr,dir]);
16899 pan.setActive(true);
16901 cur.el.removeClass([lr]);
16902 cur.setActive(false);
16904 _this.transition = false;
16906 }, this, { single: true } );
16911 cur.setActive(false);
16912 pan.setActive(true);
16917 showPanelNext : function()
16919 var i = this.indexOfPanel(this.getActivePanel());
16921 if (i >= this.tabs.length - 1 && !this.autoslide) {
16925 if (i >= this.tabs.length - 1 && this.autoslide) {
16929 this.showPanel(this.tabs[i+1]);
16932 showPanelPrev : function()
16934 var i = this.indexOfPanel(this.getActivePanel());
16936 if (i < 1 && !this.autoslide) {
16940 if (i < 1 && this.autoslide) {
16941 i = this.tabs.length;
16944 this.showPanel(this.tabs[i-1]);
16948 addBullet: function()
16950 if(!this.bullets || Roo.isTouch){
16953 var ctr = this.el.select('.carousel-bullets',true).first();
16954 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16955 var bullet = ctr.createChild({
16956 cls : 'bullet bullet-' + i
16957 },ctr.dom.lastChild);
16962 bullet.on('click', (function(e, el, o, ii, t){
16964 e.preventDefault();
16966 this.showPanel(ii);
16968 if(this.autoslide && this.slideFn){
16969 clearInterval(this.slideFn);
16970 this.slideFn = window.setInterval(function() {
16971 _this.showPanelNext();
16975 }).createDelegate(this, [i, bullet], true));
16980 setActiveBullet : function(i)
16986 Roo.each(this.el.select('.bullet', true).elements, function(el){
16987 el.removeClass('selected');
16990 var bullet = this.el.select('.bullet-' + i, true).first();
16996 bullet.addClass('selected');
17007 Roo.apply(Roo.bootstrap.TabGroup, {
17011 * register a Navigation Group
17012 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17014 register : function(navgrp)
17016 this.groups[navgrp.navId] = navgrp;
17020 * fetch a Navigation Group based on the navigation ID
17021 * if one does not exist , it will get created.
17022 * @param {string} the navgroup to add
17023 * @returns {Roo.bootstrap.NavGroup} the navgroup
17025 get: function(navId) {
17026 if (typeof(this.groups[navId]) == 'undefined') {
17027 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17029 return this.groups[navId] ;
17044 * @class Roo.bootstrap.TabPanel
17045 * @extends Roo.bootstrap.Component
17046 * Bootstrap TabPanel class
17047 * @cfg {Boolean} active panel active
17048 * @cfg {String} html panel content
17049 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17050 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17051 * @cfg {String} href click to link..
17055 * Create a new TabPanel
17056 * @param {Object} config The config object
17059 Roo.bootstrap.TabPanel = function(config){
17060 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17064 * Fires when the active status changes
17065 * @param {Roo.bootstrap.TabPanel} this
17066 * @param {Boolean} state the new state
17071 * @event beforedeactivate
17072 * Fires before a tab is de-activated - can be used to do validation on a form.
17073 * @param {Roo.bootstrap.TabPanel} this
17074 * @return {Boolean} false if there is an error
17077 'beforedeactivate': true
17080 this.tabId = this.tabId || Roo.id();
17084 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17092 getAutoCreate : function(){
17095 // item is needed for carousel - not sure if it has any effect otherwise
17096 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17097 html: this.html || ''
17101 cfg.cls += ' active';
17105 cfg.tabId = this.tabId;
17112 initEvents: function()
17114 var p = this.parent();
17115 this.navId = this.navId || p.navId;
17117 if (typeof(this.navId) != 'undefined') {
17118 // not really needed.. but just in case.. parent should be a NavGroup.
17119 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17123 var i = tg.tabs.length - 1;
17125 if(this.active && tg.bullets > 0 && i < tg.bullets){
17126 tg.setActiveBullet(i);
17130 if(this.href.length){
17131 this.el.on('click', this.onClick, this);
17136 onRender : function(ct, position)
17138 // Roo.log("Call onRender: " + this.xtype);
17140 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17148 setActive: function(state)
17150 Roo.log("panel - set active " + this.tabId + "=" + state);
17152 this.active = state;
17154 this.el.removeClass('active');
17156 } else if (!this.el.hasClass('active')) {
17157 this.el.addClass('active');
17160 this.fireEvent('changed', this, state);
17163 onClick: function(e)
17165 e.preventDefault();
17167 window.location.href = this.href;
17184 * @class Roo.bootstrap.DateField
17185 * @extends Roo.bootstrap.Input
17186 * Bootstrap DateField class
17187 * @cfg {Number} weekStart default 0
17188 * @cfg {String} viewMode default empty, (months|years)
17189 * @cfg {String} minViewMode default empty, (months|years)
17190 * @cfg {Number} startDate default -Infinity
17191 * @cfg {Number} endDate default Infinity
17192 * @cfg {Boolean} todayHighlight default false
17193 * @cfg {Boolean} todayBtn default false
17194 * @cfg {Boolean} calendarWeeks default false
17195 * @cfg {Object} daysOfWeekDisabled default empty
17196 * @cfg {Boolean} singleMode default false (true | false)
17198 * @cfg {Boolean} keyboardNavigation default true
17199 * @cfg {String} language default en
17202 * Create a new DateField
17203 * @param {Object} config The config object
17206 Roo.bootstrap.DateField = function(config){
17207 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17211 * Fires when this field show.
17212 * @param {Roo.bootstrap.DateField} this
17213 * @param {Mixed} date The date value
17218 * Fires when this field hide.
17219 * @param {Roo.bootstrap.DateField} this
17220 * @param {Mixed} date The date value
17225 * Fires when select a date.
17226 * @param {Roo.bootstrap.DateField} this
17227 * @param {Mixed} date The date value
17231 * @event beforeselect
17232 * Fires when before select a date.
17233 * @param {Roo.bootstrap.DateField} this
17234 * @param {Mixed} date The date value
17236 beforeselect : true
17240 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17243 * @cfg {String} format
17244 * The default date format string which can be overriden for localization support. The format must be
17245 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17249 * @cfg {String} altFormats
17250 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17251 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17253 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17261 todayHighlight : false,
17267 keyboardNavigation: true,
17269 calendarWeeks: false,
17271 startDate: -Infinity,
17275 daysOfWeekDisabled: [],
17279 singleMode : false,
17281 UTCDate: function()
17283 return new Date(Date.UTC.apply(Date, arguments));
17286 UTCToday: function()
17288 var today = new Date();
17289 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17292 getDate: function() {
17293 var d = this.getUTCDate();
17294 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17297 getUTCDate: function() {
17301 setDate: function(d) {
17302 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17305 setUTCDate: function(d) {
17307 this.setValue(this.formatDate(this.date));
17310 onRender: function(ct, position)
17313 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17315 this.language = this.language || 'en';
17316 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17317 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17319 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17320 this.format = this.format || 'm/d/y';
17321 this.isInline = false;
17322 this.isInput = true;
17323 this.component = this.el.select('.add-on', true).first() || false;
17324 this.component = (this.component && this.component.length === 0) ? false : this.component;
17325 this.hasInput = this.component && this.inputEL().length;
17327 if (typeof(this.minViewMode === 'string')) {
17328 switch (this.minViewMode) {
17330 this.minViewMode = 1;
17333 this.minViewMode = 2;
17336 this.minViewMode = 0;
17341 if (typeof(this.viewMode === 'string')) {
17342 switch (this.viewMode) {
17355 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17357 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17359 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17361 this.picker().on('mousedown', this.onMousedown, this);
17362 this.picker().on('click', this.onClick, this);
17364 this.picker().addClass('datepicker-dropdown');
17366 this.startViewMode = this.viewMode;
17368 if(this.singleMode){
17369 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17370 v.setVisibilityMode(Roo.Element.DISPLAY);
17374 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17375 v.setStyle('width', '189px');
17379 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17380 if(!this.calendarWeeks){
17385 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17386 v.attr('colspan', function(i, val){
17387 return parseInt(val) + 1;
17392 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17394 this.setStartDate(this.startDate);
17395 this.setEndDate(this.endDate);
17397 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17404 if(this.isInline) {
17409 picker : function()
17411 return this.pickerEl;
17412 // return this.el.select('.datepicker', true).first();
17415 fillDow: function()
17417 var dowCnt = this.weekStart;
17426 if(this.calendarWeeks){
17434 while (dowCnt < this.weekStart + 7) {
17438 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17442 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17445 fillMonths: function()
17448 var months = this.picker().select('>.datepicker-months td', true).first();
17450 months.dom.innerHTML = '';
17456 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17459 months.createChild(month);
17466 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;
17468 if (this.date < this.startDate) {
17469 this.viewDate = new Date(this.startDate);
17470 } else if (this.date > this.endDate) {
17471 this.viewDate = new Date(this.endDate);
17473 this.viewDate = new Date(this.date);
17481 var d = new Date(this.viewDate),
17482 year = d.getUTCFullYear(),
17483 month = d.getUTCMonth(),
17484 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17485 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17486 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17487 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17488 currentDate = this.date && this.date.valueOf(),
17489 today = this.UTCToday();
17491 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17493 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17495 // this.picker.select('>tfoot th.today').
17496 // .text(dates[this.language].today)
17497 // .toggle(this.todayBtn !== false);
17499 this.updateNavArrows();
17502 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17504 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17506 prevMonth.setUTCDate(day);
17508 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17510 var nextMonth = new Date(prevMonth);
17512 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17514 nextMonth = nextMonth.valueOf();
17516 var fillMonths = false;
17518 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17520 while(prevMonth.valueOf() < nextMonth) {
17523 if (prevMonth.getUTCDay() === this.weekStart) {
17525 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17533 if(this.calendarWeeks){
17534 // ISO 8601: First week contains first thursday.
17535 // ISO also states week starts on Monday, but we can be more abstract here.
17537 // Start of current week: based on weekstart/current date
17538 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17539 // Thursday of this week
17540 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17541 // First Thursday of year, year from thursday
17542 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17543 // Calendar week: ms between thursdays, div ms per day, div 7 days
17544 calWeek = (th - yth) / 864e5 / 7 + 1;
17546 fillMonths.cn.push({
17554 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17556 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17559 if (this.todayHighlight &&
17560 prevMonth.getUTCFullYear() == today.getFullYear() &&
17561 prevMonth.getUTCMonth() == today.getMonth() &&
17562 prevMonth.getUTCDate() == today.getDate()) {
17563 clsName += ' today';
17566 if (currentDate && prevMonth.valueOf() === currentDate) {
17567 clsName += ' active';
17570 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17571 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17572 clsName += ' disabled';
17575 fillMonths.cn.push({
17577 cls: 'day ' + clsName,
17578 html: prevMonth.getDate()
17581 prevMonth.setDate(prevMonth.getDate()+1);
17584 var currentYear = this.date && this.date.getUTCFullYear();
17585 var currentMonth = this.date && this.date.getUTCMonth();
17587 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17589 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17590 v.removeClass('active');
17592 if(currentYear === year && k === currentMonth){
17593 v.addClass('active');
17596 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17597 v.addClass('disabled');
17603 year = parseInt(year/10, 10) * 10;
17605 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17607 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17610 for (var i = -1; i < 11; i++) {
17611 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17613 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17621 showMode: function(dir)
17624 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17627 Roo.each(this.picker().select('>div',true).elements, function(v){
17628 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17631 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17636 if(this.isInline) {
17640 this.picker().removeClass(['bottom', 'top']);
17642 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17644 * place to the top of element!
17648 this.picker().addClass('top');
17649 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17654 this.picker().addClass('bottom');
17656 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17659 parseDate : function(value)
17661 if(!value || value instanceof Date){
17664 var v = Date.parseDate(value, this.format);
17665 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17666 v = Date.parseDate(value, 'Y-m-d');
17668 if(!v && this.altFormats){
17669 if(!this.altFormatsArray){
17670 this.altFormatsArray = this.altFormats.split("|");
17672 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17673 v = Date.parseDate(value, this.altFormatsArray[i]);
17679 formatDate : function(date, fmt)
17681 return (!date || !(date instanceof Date)) ?
17682 date : date.dateFormat(fmt || this.format);
17685 onFocus : function()
17687 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17691 onBlur : function()
17693 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17695 var d = this.inputEl().getValue();
17704 this.picker().show();
17708 this.fireEvent('show', this, this.date);
17713 if(this.isInline) {
17716 this.picker().hide();
17717 this.viewMode = this.startViewMode;
17720 this.fireEvent('hide', this, this.date);
17724 onMousedown: function(e)
17726 e.stopPropagation();
17727 e.preventDefault();
17732 Roo.bootstrap.DateField.superclass.keyup.call(this);
17736 setValue: function(v)
17738 if(this.fireEvent('beforeselect', this, v) !== false){
17739 var d = new Date(this.parseDate(v) ).clearTime();
17741 if(isNaN(d.getTime())){
17742 this.date = this.viewDate = '';
17743 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17747 v = this.formatDate(d);
17749 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17751 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17755 this.fireEvent('select', this, this.date);
17759 getValue: function()
17761 return this.formatDate(this.date);
17764 fireKey: function(e)
17766 if (!this.picker().isVisible()){
17767 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17773 var dateChanged = false,
17775 newDate, newViewDate;
17780 e.preventDefault();
17784 if (!this.keyboardNavigation) {
17787 dir = e.keyCode == 37 ? -1 : 1;
17790 newDate = this.moveYear(this.date, dir);
17791 newViewDate = this.moveYear(this.viewDate, dir);
17792 } else if (e.shiftKey){
17793 newDate = this.moveMonth(this.date, dir);
17794 newViewDate = this.moveMonth(this.viewDate, dir);
17796 newDate = new Date(this.date);
17797 newDate.setUTCDate(this.date.getUTCDate() + dir);
17798 newViewDate = new Date(this.viewDate);
17799 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17801 if (this.dateWithinRange(newDate)){
17802 this.date = newDate;
17803 this.viewDate = newViewDate;
17804 this.setValue(this.formatDate(this.date));
17806 e.preventDefault();
17807 dateChanged = true;
17812 if (!this.keyboardNavigation) {
17815 dir = e.keyCode == 38 ? -1 : 1;
17817 newDate = this.moveYear(this.date, dir);
17818 newViewDate = this.moveYear(this.viewDate, dir);
17819 } else if (e.shiftKey){
17820 newDate = this.moveMonth(this.date, dir);
17821 newViewDate = this.moveMonth(this.viewDate, dir);
17823 newDate = new Date(this.date);
17824 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17825 newViewDate = new Date(this.viewDate);
17826 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17828 if (this.dateWithinRange(newDate)){
17829 this.date = newDate;
17830 this.viewDate = newViewDate;
17831 this.setValue(this.formatDate(this.date));
17833 e.preventDefault();
17834 dateChanged = true;
17838 this.setValue(this.formatDate(this.date));
17840 e.preventDefault();
17843 this.setValue(this.formatDate(this.date));
17857 onClick: function(e)
17859 e.stopPropagation();
17860 e.preventDefault();
17862 var target = e.getTarget();
17864 if(target.nodeName.toLowerCase() === 'i'){
17865 target = Roo.get(target).dom.parentNode;
17868 var nodeName = target.nodeName;
17869 var className = target.className;
17870 var html = target.innerHTML;
17871 //Roo.log(nodeName);
17873 switch(nodeName.toLowerCase()) {
17875 switch(className) {
17881 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17882 switch(this.viewMode){
17884 this.viewDate = this.moveMonth(this.viewDate, dir);
17888 this.viewDate = this.moveYear(this.viewDate, dir);
17894 var date = new Date();
17895 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17897 this.setValue(this.formatDate(this.date));
17904 if (className.indexOf('disabled') < 0) {
17905 this.viewDate.setUTCDate(1);
17906 if (className.indexOf('month') > -1) {
17907 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17909 var year = parseInt(html, 10) || 0;
17910 this.viewDate.setUTCFullYear(year);
17914 if(this.singleMode){
17915 this.setValue(this.formatDate(this.viewDate));
17926 //Roo.log(className);
17927 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17928 var day = parseInt(html, 10) || 1;
17929 var year = this.viewDate.getUTCFullYear(),
17930 month = this.viewDate.getUTCMonth();
17932 if (className.indexOf('old') > -1) {
17939 } else if (className.indexOf('new') > -1) {
17947 //Roo.log([year,month,day]);
17948 this.date = this.UTCDate(year, month, day,0,0,0,0);
17949 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17951 //Roo.log(this.formatDate(this.date));
17952 this.setValue(this.formatDate(this.date));
17959 setStartDate: function(startDate)
17961 this.startDate = startDate || -Infinity;
17962 if (this.startDate !== -Infinity) {
17963 this.startDate = this.parseDate(this.startDate);
17966 this.updateNavArrows();
17969 setEndDate: function(endDate)
17971 this.endDate = endDate || Infinity;
17972 if (this.endDate !== Infinity) {
17973 this.endDate = this.parseDate(this.endDate);
17976 this.updateNavArrows();
17979 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17981 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17982 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17983 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17985 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17986 return parseInt(d, 10);
17989 this.updateNavArrows();
17992 updateNavArrows: function()
17994 if(this.singleMode){
17998 var d = new Date(this.viewDate),
17999 year = d.getUTCFullYear(),
18000 month = d.getUTCMonth();
18002 Roo.each(this.picker().select('.prev', true).elements, function(v){
18004 switch (this.viewMode) {
18007 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18013 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18020 Roo.each(this.picker().select('.next', true).elements, function(v){
18022 switch (this.viewMode) {
18025 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18031 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18039 moveMonth: function(date, dir)
18044 var new_date = new Date(date.valueOf()),
18045 day = new_date.getUTCDate(),
18046 month = new_date.getUTCMonth(),
18047 mag = Math.abs(dir),
18049 dir = dir > 0 ? 1 : -1;
18052 // If going back one month, make sure month is not current month
18053 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18055 return new_date.getUTCMonth() == month;
18057 // If going forward one month, make sure month is as expected
18058 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18060 return new_date.getUTCMonth() != new_month;
18062 new_month = month + dir;
18063 new_date.setUTCMonth(new_month);
18064 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18065 if (new_month < 0 || new_month > 11) {
18066 new_month = (new_month + 12) % 12;
18069 // For magnitudes >1, move one month at a time...
18070 for (var i=0; i<mag; i++) {
18071 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18072 new_date = this.moveMonth(new_date, dir);
18074 // ...then reset the day, keeping it in the new month
18075 new_month = new_date.getUTCMonth();
18076 new_date.setUTCDate(day);
18078 return new_month != new_date.getUTCMonth();
18081 // Common date-resetting loop -- if date is beyond end of month, make it
18084 new_date.setUTCDate(--day);
18085 new_date.setUTCMonth(new_month);
18090 moveYear: function(date, dir)
18092 return this.moveMonth(date, dir*12);
18095 dateWithinRange: function(date)
18097 return date >= this.startDate && date <= this.endDate;
18103 this.picker().remove();
18108 Roo.apply(Roo.bootstrap.DateField, {
18119 html: '<i class="fa fa-arrow-left"/>'
18129 html: '<i class="fa fa-arrow-right"/>'
18171 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18172 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18173 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18174 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18175 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18188 navFnc: 'FullYear',
18193 navFnc: 'FullYear',
18198 Roo.apply(Roo.bootstrap.DateField, {
18202 cls: 'datepicker dropdown-menu roo-dynamic',
18206 cls: 'datepicker-days',
18210 cls: 'table-condensed',
18212 Roo.bootstrap.DateField.head,
18216 Roo.bootstrap.DateField.footer
18223 cls: 'datepicker-months',
18227 cls: 'table-condensed',
18229 Roo.bootstrap.DateField.head,
18230 Roo.bootstrap.DateField.content,
18231 Roo.bootstrap.DateField.footer
18238 cls: 'datepicker-years',
18242 cls: 'table-condensed',
18244 Roo.bootstrap.DateField.head,
18245 Roo.bootstrap.DateField.content,
18246 Roo.bootstrap.DateField.footer
18265 * @class Roo.bootstrap.TimeField
18266 * @extends Roo.bootstrap.Input
18267 * Bootstrap DateField class
18271 * Create a new TimeField
18272 * @param {Object} config The config object
18275 Roo.bootstrap.TimeField = function(config){
18276 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18280 * Fires when this field show.
18281 * @param {Roo.bootstrap.DateField} thisthis
18282 * @param {Mixed} date The date value
18287 * Fires when this field hide.
18288 * @param {Roo.bootstrap.DateField} this
18289 * @param {Mixed} date The date value
18294 * Fires when select a date.
18295 * @param {Roo.bootstrap.DateField} this
18296 * @param {Mixed} date The date value
18302 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18305 * @cfg {String} format
18306 * The default time format string which can be overriden for localization support. The format must be
18307 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18311 onRender: function(ct, position)
18314 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18316 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18318 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18320 this.pop = this.picker().select('>.datepicker-time',true).first();
18321 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18323 this.picker().on('mousedown', this.onMousedown, this);
18324 this.picker().on('click', this.onClick, this);
18326 this.picker().addClass('datepicker-dropdown');
18331 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18332 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18333 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18334 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18335 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18336 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18340 fireKey: function(e){
18341 if (!this.picker().isVisible()){
18342 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18348 e.preventDefault();
18356 this.onTogglePeriod();
18359 this.onIncrementMinutes();
18362 this.onDecrementMinutes();
18371 onClick: function(e) {
18372 e.stopPropagation();
18373 e.preventDefault();
18376 picker : function()
18378 return this.el.select('.datepicker', true).first();
18381 fillTime: function()
18383 var time = this.pop.select('tbody', true).first();
18385 time.dom.innerHTML = '';
18400 cls: 'hours-up glyphicon glyphicon-chevron-up'
18420 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18441 cls: 'timepicker-hour',
18456 cls: 'timepicker-minute',
18471 cls: 'btn btn-primary period',
18493 cls: 'hours-down glyphicon glyphicon-chevron-down'
18513 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18531 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18538 var hours = this.time.getHours();
18539 var minutes = this.time.getMinutes();
18552 hours = hours - 12;
18556 hours = '0' + hours;
18560 minutes = '0' + minutes;
18563 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18564 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18565 this.pop.select('button', true).first().dom.innerHTML = period;
18571 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18573 var cls = ['bottom'];
18575 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18582 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18587 this.picker().addClass(cls.join('-'));
18591 Roo.each(cls, function(c){
18593 _this.picker().setTop(_this.inputEl().getHeight());
18597 _this.picker().setTop(0 - _this.picker().getHeight());
18602 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18606 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18613 onFocus : function()
18615 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18619 onBlur : function()
18621 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18627 this.picker().show();
18632 this.fireEvent('show', this, this.date);
18637 this.picker().hide();
18640 this.fireEvent('hide', this, this.date);
18643 setTime : function()
18646 this.setValue(this.time.format(this.format));
18648 this.fireEvent('select', this, this.date);
18653 onMousedown: function(e){
18654 e.stopPropagation();
18655 e.preventDefault();
18658 onIncrementHours: function()
18660 Roo.log('onIncrementHours');
18661 this.time = this.time.add(Date.HOUR, 1);
18666 onDecrementHours: function()
18668 Roo.log('onDecrementHours');
18669 this.time = this.time.add(Date.HOUR, -1);
18673 onIncrementMinutes: function()
18675 Roo.log('onIncrementMinutes');
18676 this.time = this.time.add(Date.MINUTE, 1);
18680 onDecrementMinutes: function()
18682 Roo.log('onDecrementMinutes');
18683 this.time = this.time.add(Date.MINUTE, -1);
18687 onTogglePeriod: function()
18689 Roo.log('onTogglePeriod');
18690 this.time = this.time.add(Date.HOUR, 12);
18697 Roo.apply(Roo.bootstrap.TimeField, {
18727 cls: 'btn btn-info ok',
18739 Roo.apply(Roo.bootstrap.TimeField, {
18743 cls: 'datepicker dropdown-menu',
18747 cls: 'datepicker-time',
18751 cls: 'table-condensed',
18753 Roo.bootstrap.TimeField.content,
18754 Roo.bootstrap.TimeField.footer
18773 * @class Roo.bootstrap.MonthField
18774 * @extends Roo.bootstrap.Input
18775 * Bootstrap MonthField class
18777 * @cfg {String} language default en
18780 * Create a new MonthField
18781 * @param {Object} config The config object
18784 Roo.bootstrap.MonthField = function(config){
18785 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18790 * Fires when this field show.
18791 * @param {Roo.bootstrap.MonthField} this
18792 * @param {Mixed} date The date value
18797 * Fires when this field hide.
18798 * @param {Roo.bootstrap.MonthField} this
18799 * @param {Mixed} date The date value
18804 * Fires when select a date.
18805 * @param {Roo.bootstrap.MonthField} this
18806 * @param {String} oldvalue The old value
18807 * @param {String} newvalue The new value
18813 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18815 onRender: function(ct, position)
18818 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18820 this.language = this.language || 'en';
18821 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18822 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18824 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18825 this.isInline = false;
18826 this.isInput = true;
18827 this.component = this.el.select('.add-on', true).first() || false;
18828 this.component = (this.component && this.component.length === 0) ? false : this.component;
18829 this.hasInput = this.component && this.inputEL().length;
18831 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18833 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18835 this.picker().on('mousedown', this.onMousedown, this);
18836 this.picker().on('click', this.onClick, this);
18838 this.picker().addClass('datepicker-dropdown');
18840 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18841 v.setStyle('width', '189px');
18848 if(this.isInline) {
18854 setValue: function(v, suppressEvent)
18856 var o = this.getValue();
18858 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18862 if(suppressEvent !== true){
18863 this.fireEvent('select', this, o, v);
18868 getValue: function()
18873 onClick: function(e)
18875 e.stopPropagation();
18876 e.preventDefault();
18878 var target = e.getTarget();
18880 if(target.nodeName.toLowerCase() === 'i'){
18881 target = Roo.get(target).dom.parentNode;
18884 var nodeName = target.nodeName;
18885 var className = target.className;
18886 var html = target.innerHTML;
18888 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18892 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18894 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18900 picker : function()
18902 return this.pickerEl;
18905 fillMonths: function()
18908 var months = this.picker().select('>.datepicker-months td', true).first();
18910 months.dom.innerHTML = '';
18916 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18919 months.createChild(month);
18928 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18929 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18932 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18933 e.removeClass('active');
18935 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18936 e.addClass('active');
18943 if(this.isInline) {
18947 this.picker().removeClass(['bottom', 'top']);
18949 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18951 * place to the top of element!
18955 this.picker().addClass('top');
18956 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18961 this.picker().addClass('bottom');
18963 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18966 onFocus : function()
18968 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18972 onBlur : function()
18974 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18976 var d = this.inputEl().getValue();
18985 this.picker().show();
18986 this.picker().select('>.datepicker-months', true).first().show();
18990 this.fireEvent('show', this, this.date);
18995 if(this.isInline) {
18998 this.picker().hide();
18999 this.fireEvent('hide', this, this.date);
19003 onMousedown: function(e)
19005 e.stopPropagation();
19006 e.preventDefault();
19011 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19015 fireKey: function(e)
19017 if (!this.picker().isVisible()){
19018 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19029 e.preventDefault();
19033 dir = e.keyCode == 37 ? -1 : 1;
19035 this.vIndex = this.vIndex + dir;
19037 if(this.vIndex < 0){
19041 if(this.vIndex > 11){
19045 if(isNaN(this.vIndex)){
19049 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19055 dir = e.keyCode == 38 ? -1 : 1;
19057 this.vIndex = this.vIndex + dir * 4;
19059 if(this.vIndex < 0){
19063 if(this.vIndex > 11){
19067 if(isNaN(this.vIndex)){
19071 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19076 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19077 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19081 e.preventDefault();
19084 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19085 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19101 this.picker().remove();
19106 Roo.apply(Roo.bootstrap.MonthField, {
19125 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19126 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19131 Roo.apply(Roo.bootstrap.MonthField, {
19135 cls: 'datepicker dropdown-menu roo-dynamic',
19139 cls: 'datepicker-months',
19143 cls: 'table-condensed',
19145 Roo.bootstrap.DateField.content
19165 * @class Roo.bootstrap.CheckBox
19166 * @extends Roo.bootstrap.Input
19167 * Bootstrap CheckBox class
19169 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19170 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19171 * @cfg {String} boxLabel The text that appears beside the checkbox
19172 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19173 * @cfg {Boolean} checked initnal the element
19174 * @cfg {Boolean} inline inline the element (default false)
19175 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19178 * Create a new CheckBox
19179 * @param {Object} config The config object
19182 Roo.bootstrap.CheckBox = function(config){
19183 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19188 * Fires when the element is checked or unchecked.
19189 * @param {Roo.bootstrap.CheckBox} this This input
19190 * @param {Boolean} checked The new checked value
19197 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19199 inputType: 'checkbox',
19207 getAutoCreate : function()
19209 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19215 cfg.cls = 'form-group ' + this.inputType; //input-group
19218 cfg.cls += ' ' + this.inputType + '-inline';
19224 type : this.inputType,
19225 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19226 cls : 'roo-' + this.inputType, //'form-box',
19227 placeholder : this.placeholder || ''
19231 if (this.weight) { // Validity check?
19232 cfg.cls += " " + this.inputType + "-" + this.weight;
19235 if (this.disabled) {
19236 input.disabled=true;
19240 input.checked = this.checked;
19244 input.name = this.name;
19248 input.cls += ' input-' + this.size;
19253 ['xs','sm','md','lg'].map(function(size){
19254 if (settings[size]) {
19255 cfg.cls += ' col-' + size + '-' + settings[size];
19259 var inputblock = input;
19261 if (this.before || this.after) {
19264 cls : 'input-group',
19269 inputblock.cn.push({
19271 cls : 'input-group-addon',
19276 inputblock.cn.push(input);
19279 inputblock.cn.push({
19281 cls : 'input-group-addon',
19288 if (align ==='left' && this.fieldLabel.length) {
19289 // Roo.log("left and has label");
19295 cls : 'control-label col-md-' + this.labelWidth,
19296 html : this.fieldLabel
19300 cls : "col-md-" + (12 - this.labelWidth),
19307 } else if ( this.fieldLabel.length) {
19308 // Roo.log(" label");
19312 tag: this.boxLabel ? 'span' : 'label',
19314 cls: 'control-label box-input-label',
19315 //cls : 'input-group-addon',
19316 html : this.fieldLabel
19326 // Roo.log(" no label && no align");
19327 cfg.cn = [ inputblock ] ;
19333 var boxLabelCfg = {
19335 //'for': id, // box label is handled by onclick - so no for...
19337 html: this.boxLabel
19341 boxLabelCfg.tooltip = this.tooltip;
19344 cfg.cn.push(boxLabelCfg);
19354 * return the real input element.
19356 inputEl: function ()
19358 return this.el.select('input.roo-' + this.inputType,true).first();
19361 labelEl: function()
19363 return this.el.select('label.control-label',true).first();
19365 /* depricated... */
19369 return this.labelEl();
19372 boxLabelEl: function()
19374 return this.el.select('label.box-label',true).first();
19377 initEvents : function()
19379 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19381 this.inputEl().on('click', this.onClick, this);
19383 if (this.boxLabel) {
19384 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19387 this.startValue = this.getValue();
19390 Roo.bootstrap.CheckBox.register(this);
19394 onClick : function()
19396 this.setChecked(!this.checked);
19399 setChecked : function(state,suppressEvent)
19401 this.startValue = this.getValue();
19403 if(this.inputType == 'radio'){
19405 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19406 e.dom.checked = false;
19409 this.inputEl().dom.checked = true;
19411 this.inputEl().dom.value = this.inputValue;
19413 if(suppressEvent !== true){
19414 this.fireEvent('check', this, true);
19422 this.checked = state;
19424 this.inputEl().dom.checked = state;
19426 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19428 if(suppressEvent !== true){
19429 this.fireEvent('check', this, state);
19435 getValue : function()
19437 if(this.inputType == 'radio'){
19438 return this.getGroupValue();
19441 return this.inputEl().getValue();
19445 getGroupValue : function()
19447 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19451 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19454 setValue : function(v,suppressEvent)
19456 if(this.inputType == 'radio'){
19457 this.setGroupValue(v, suppressEvent);
19461 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19466 setGroupValue : function(v, suppressEvent)
19468 this.startValue = this.getValue();
19470 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19471 e.dom.checked = false;
19473 if(e.dom.value == v){
19474 e.dom.checked = true;
19478 if(suppressEvent !== true){
19479 this.fireEvent('check', this, true);
19487 validate : function()
19491 (this.inputType == 'radio' && this.validateRadio()) ||
19492 (this.inputType == 'checkbox' && this.validateCheckbox())
19498 this.markInvalid();
19502 validateRadio : function()
19506 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19507 if(!e.dom.checked){
19519 validateCheckbox : function()
19522 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19525 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19533 for(var i in group){
19538 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19545 * Mark this field as valid
19547 markValid : function()
19549 if(this.allowBlank){
19555 this.fireEvent('valid', this);
19557 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19560 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19567 if(this.inputType == 'radio'){
19568 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19569 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19570 e.findParent('.form-group', false, true).addClass(_this.validClass);
19577 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19578 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19582 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19588 for(var i in group){
19589 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19590 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19595 * Mark this field as invalid
19596 * @param {String} msg The validation message
19598 markInvalid : function(msg)
19600 if(this.allowBlank){
19606 this.fireEvent('invalid', this, msg);
19608 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19611 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19615 label.markInvalid();
19618 if(this.inputType == 'radio'){
19619 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19620 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19621 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19628 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19629 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19633 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19639 for(var i in group){
19640 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19641 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19648 Roo.apply(Roo.bootstrap.CheckBox, {
19653 * register a CheckBox Group
19654 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19656 register : function(checkbox)
19658 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19659 this.groups[checkbox.groupId] = {};
19662 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19666 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19670 * fetch a CheckBox Group based on the group ID
19671 * @param {string} the group ID
19672 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19674 get: function(groupId) {
19675 if (typeof(this.groups[groupId]) == 'undefined') {
19679 return this.groups[groupId] ;
19691 *<div class="radio">
19693 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19694 Option one is this and that—be sure to include why it's great
19701 *<label class="radio-inline">fieldLabel</label>
19702 *<label class="radio-inline">
19703 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19711 * @class Roo.bootstrap.Radio
19712 * @extends Roo.bootstrap.CheckBox
19713 * Bootstrap Radio class
19716 * Create a new Radio
19717 * @param {Object} config The config object
19720 Roo.bootstrap.Radio = function(config){
19721 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19725 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19727 inputType: 'radio',
19731 getAutoCreate : function()
19733 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19734 align = align || 'left'; // default...
19741 tag : this.inline ? 'span' : 'div',
19746 var inline = this.inline ? ' radio-inline' : '';
19750 // does not need for, as we wrap the input with it..
19752 cls : 'control-label box-label' + inline,
19755 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19759 //cls : 'control-label' + inline,
19760 html : this.fieldLabel,
19761 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19770 type : this.inputType,
19771 //value : (!this.checked) ? this.valueOff : this.inputValue,
19772 value : this.inputValue,
19774 placeholder : this.placeholder || '' // ?? needed????
19777 if (this.weight) { // Validity check?
19778 input.cls += " radio-" + this.weight;
19780 if (this.disabled) {
19781 input.disabled=true;
19785 input.checked = this.checked;
19789 input.name = this.name;
19793 input.cls += ' input-' + this.size;
19796 //?? can span's inline have a width??
19799 ['xs','sm','md','lg'].map(function(size){
19800 if (settings[size]) {
19801 cfg.cls += ' col-' + size + '-' + settings[size];
19805 var inputblock = input;
19807 if (this.before || this.after) {
19810 cls : 'input-group',
19815 inputblock.cn.push({
19817 cls : 'input-group-addon',
19821 inputblock.cn.push(input);
19823 inputblock.cn.push({
19825 cls : 'input-group-addon',
19833 if (this.fieldLabel && this.fieldLabel.length) {
19834 cfg.cn.push(fieldLabel);
19837 // normal bootstrap puts the input inside the label.
19838 // however with our styled version - it has to go after the input.
19840 //lbl.cn.push(inputblock);
19844 cls: 'radio' + inline,
19851 cfg.cn.push( lblwrap);
19856 html: this.boxLabel
19865 initEvents : function()
19867 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19869 this.inputEl().on('click', this.onClick, this);
19870 if (this.boxLabel) {
19871 //Roo.log('find label');
19872 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19877 inputEl: function ()
19879 return this.el.select('input.roo-radio',true).first();
19881 onClick : function()
19884 this.setChecked(true);
19887 setChecked : function(state,suppressEvent)
19890 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19891 v.dom.checked = false;
19894 Roo.log(this.inputEl().dom);
19895 this.checked = state;
19896 this.inputEl().dom.checked = state;
19898 if(suppressEvent !== true){
19899 this.fireEvent('check', this, state);
19902 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19906 getGroupValue : function()
19909 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19910 if(v.dom.checked == true){
19911 value = v.dom.value;
19919 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19920 * @return {Mixed} value The field value
19922 getValue : function(){
19923 return this.getGroupValue();
19929 //<script type="text/javascript">
19932 * Based Ext JS Library 1.1.1
19933 * Copyright(c) 2006-2007, Ext JS, LLC.
19939 * @class Roo.HtmlEditorCore
19940 * @extends Roo.Component
19941 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19943 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19946 Roo.HtmlEditorCore = function(config){
19949 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19954 * @event initialize
19955 * Fires when the editor is fully initialized (including the iframe)
19956 * @param {Roo.HtmlEditorCore} this
19961 * Fires when the editor is first receives the focus. Any insertion must wait
19962 * until after this event.
19963 * @param {Roo.HtmlEditorCore} this
19967 * @event beforesync
19968 * Fires before the textarea is updated with content from the editor iframe. Return false
19969 * to cancel the sync.
19970 * @param {Roo.HtmlEditorCore} this
19971 * @param {String} html
19975 * @event beforepush
19976 * Fires before the iframe editor is updated with content from the textarea. Return false
19977 * to cancel the push.
19978 * @param {Roo.HtmlEditorCore} this
19979 * @param {String} html
19984 * Fires when the textarea is updated with content from the editor iframe.
19985 * @param {Roo.HtmlEditorCore} this
19986 * @param {String} html
19991 * Fires when the iframe editor is updated with content from the textarea.
19992 * @param {Roo.HtmlEditorCore} this
19993 * @param {String} html
19998 * @event editorevent
19999 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20000 * @param {Roo.HtmlEditorCore} this
20006 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20008 // defaults : white / black...
20009 this.applyBlacklists();
20016 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20020 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20026 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20031 * @cfg {Number} height (in pixels)
20035 * @cfg {Number} width (in pixels)
20040 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20043 stylesheets: false,
20048 // private properties
20049 validationEvent : false,
20051 initialized : false,
20053 sourceEditMode : false,
20054 onFocus : Roo.emptyFn,
20056 hideMode:'offsets',
20060 // blacklist + whitelisted elements..
20067 * Protected method that will not generally be called directly. It
20068 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20069 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20071 getDocMarkup : function(){
20075 // inherit styels from page...??
20076 if (this.stylesheets === false) {
20078 Roo.get(document.head).select('style').each(function(node) {
20079 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20082 Roo.get(document.head).select('link').each(function(node) {
20083 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20086 } else if (!this.stylesheets.length) {
20088 st = '<style type="text/css">' +
20089 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20095 st += '<style type="text/css">' +
20096 'IMG { cursor: pointer } ' +
20100 return '<html><head>' + st +
20101 //<style type="text/css">' +
20102 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20104 ' </head><body class="roo-htmleditor-body"></body></html>';
20108 onRender : function(ct, position)
20111 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20112 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20115 this.el.dom.style.border = '0 none';
20116 this.el.dom.setAttribute('tabIndex', -1);
20117 this.el.addClass('x-hidden hide');
20121 if(Roo.isIE){ // fix IE 1px bogus margin
20122 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20126 this.frameId = Roo.id();
20130 var iframe = this.owner.wrap.createChild({
20132 cls: 'form-control', // bootstrap..
20134 name: this.frameId,
20135 frameBorder : 'no',
20136 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20141 this.iframe = iframe.dom;
20143 this.assignDocWin();
20145 this.doc.designMode = 'on';
20148 this.doc.write(this.getDocMarkup());
20152 var task = { // must defer to wait for browser to be ready
20154 //console.log("run task?" + this.doc.readyState);
20155 this.assignDocWin();
20156 if(this.doc.body || this.doc.readyState == 'complete'){
20158 this.doc.designMode="on";
20162 Roo.TaskMgr.stop(task);
20163 this.initEditor.defer(10, this);
20170 Roo.TaskMgr.start(task);
20175 onResize : function(w, h)
20177 Roo.log('resize: ' +w + ',' + h );
20178 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20182 if(typeof w == 'number'){
20184 this.iframe.style.width = w + 'px';
20186 if(typeof h == 'number'){
20188 this.iframe.style.height = h + 'px';
20190 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20197 * Toggles the editor between standard and source edit mode.
20198 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20200 toggleSourceEdit : function(sourceEditMode){
20202 this.sourceEditMode = sourceEditMode === true;
20204 if(this.sourceEditMode){
20206 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20209 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20210 //this.iframe.className = '';
20213 //this.setSize(this.owner.wrap.getSize());
20214 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20221 * Protected method that will not generally be called directly. If you need/want
20222 * custom HTML cleanup, this is the method you should override.
20223 * @param {String} html The HTML to be cleaned
20224 * return {String} The cleaned HTML
20226 cleanHtml : function(html){
20227 html = String(html);
20228 if(html.length > 5){
20229 if(Roo.isSafari){ // strip safari nonsense
20230 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20233 if(html == ' '){
20240 * HTML Editor -> Textarea
20241 * Protected method that will not generally be called directly. Syncs the contents
20242 * of the editor iframe with the textarea.
20244 syncValue : function(){
20245 if(this.initialized){
20246 var bd = (this.doc.body || this.doc.documentElement);
20247 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20248 var html = bd.innerHTML;
20250 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20251 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20253 html = '<div style="'+m[0]+'">' + html + '</div>';
20256 html = this.cleanHtml(html);
20257 // fix up the special chars.. normaly like back quotes in word...
20258 // however we do not want to do this with chinese..
20259 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20260 var cc = b.charCodeAt();
20262 (cc >= 0x4E00 && cc < 0xA000 ) ||
20263 (cc >= 0x3400 && cc < 0x4E00 ) ||
20264 (cc >= 0xf900 && cc < 0xfb00 )
20270 if(this.owner.fireEvent('beforesync', this, html) !== false){
20271 this.el.dom.value = html;
20272 this.owner.fireEvent('sync', this, html);
20278 * Protected method that will not generally be called directly. Pushes the value of the textarea
20279 * into the iframe editor.
20281 pushValue : function(){
20282 if(this.initialized){
20283 var v = this.el.dom.value.trim();
20285 // if(v.length < 1){
20289 if(this.owner.fireEvent('beforepush', this, v) !== false){
20290 var d = (this.doc.body || this.doc.documentElement);
20292 this.cleanUpPaste();
20293 this.el.dom.value = d.innerHTML;
20294 this.owner.fireEvent('push', this, v);
20300 deferFocus : function(){
20301 this.focus.defer(10, this);
20305 focus : function(){
20306 if(this.win && !this.sourceEditMode){
20313 assignDocWin: function()
20315 var iframe = this.iframe;
20318 this.doc = iframe.contentWindow.document;
20319 this.win = iframe.contentWindow;
20321 // if (!Roo.get(this.frameId)) {
20324 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20325 // this.win = Roo.get(this.frameId).dom.contentWindow;
20327 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20331 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20332 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20337 initEditor : function(){
20338 //console.log("INIT EDITOR");
20339 this.assignDocWin();
20343 this.doc.designMode="on";
20345 this.doc.write(this.getDocMarkup());
20348 var dbody = (this.doc.body || this.doc.documentElement);
20349 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20350 // this copies styles from the containing element into thsi one..
20351 // not sure why we need all of this..
20352 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20354 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20355 //ss['background-attachment'] = 'fixed'; // w3c
20356 dbody.bgProperties = 'fixed'; // ie
20357 //Roo.DomHelper.applyStyles(dbody, ss);
20358 Roo.EventManager.on(this.doc, {
20359 //'mousedown': this.onEditorEvent,
20360 'mouseup': this.onEditorEvent,
20361 'dblclick': this.onEditorEvent,
20362 'click': this.onEditorEvent,
20363 'keyup': this.onEditorEvent,
20368 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20370 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20371 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20373 this.initialized = true;
20375 this.owner.fireEvent('initialize', this);
20380 onDestroy : function(){
20386 //for (var i =0; i < this.toolbars.length;i++) {
20387 // // fixme - ask toolbars for heights?
20388 // this.toolbars[i].onDestroy();
20391 //this.wrap.dom.innerHTML = '';
20392 //this.wrap.remove();
20397 onFirstFocus : function(){
20399 this.assignDocWin();
20402 this.activated = true;
20405 if(Roo.isGecko){ // prevent silly gecko errors
20407 var s = this.win.getSelection();
20408 if(!s.focusNode || s.focusNode.nodeType != 3){
20409 var r = s.getRangeAt(0);
20410 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20415 this.execCmd('useCSS', true);
20416 this.execCmd('styleWithCSS', false);
20419 this.owner.fireEvent('activate', this);
20423 adjustFont: function(btn){
20424 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20425 //if(Roo.isSafari){ // safari
20428 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20429 if(Roo.isSafari){ // safari
20430 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20431 v = (v < 10) ? 10 : v;
20432 v = (v > 48) ? 48 : v;
20433 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20438 v = Math.max(1, v+adjust);
20440 this.execCmd('FontSize', v );
20443 onEditorEvent : function(e)
20445 this.owner.fireEvent('editorevent', this, e);
20446 // this.updateToolbar();
20447 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20450 insertTag : function(tg)
20452 // could be a bit smarter... -> wrap the current selected tRoo..
20453 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20455 range = this.createRange(this.getSelection());
20456 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20457 wrappingNode.appendChild(range.extractContents());
20458 range.insertNode(wrappingNode);
20465 this.execCmd("formatblock", tg);
20469 insertText : function(txt)
20473 var range = this.createRange();
20474 range.deleteContents();
20475 //alert(Sender.getAttribute('label'));
20477 range.insertNode(this.doc.createTextNode(txt));
20483 * Executes a Midas editor command on the editor document and performs necessary focus and
20484 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20485 * @param {String} cmd The Midas command
20486 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20488 relayCmd : function(cmd, value){
20490 this.execCmd(cmd, value);
20491 this.owner.fireEvent('editorevent', this);
20492 //this.updateToolbar();
20493 this.owner.deferFocus();
20497 * Executes a Midas editor command directly on the editor document.
20498 * For visual commands, you should use {@link #relayCmd} instead.
20499 * <b>This should only be called after the editor is initialized.</b>
20500 * @param {String} cmd The Midas command
20501 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20503 execCmd : function(cmd, value){
20504 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20511 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20513 * @param {String} text | dom node..
20515 insertAtCursor : function(text)
20520 if(!this.activated){
20526 var r = this.doc.selection.createRange();
20537 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20541 // from jquery ui (MIT licenced)
20543 var win = this.win;
20545 if (win.getSelection && win.getSelection().getRangeAt) {
20546 range = win.getSelection().getRangeAt(0);
20547 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20548 range.insertNode(node);
20549 } else if (win.document.selection && win.document.selection.createRange) {
20550 // no firefox support
20551 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20552 win.document.selection.createRange().pasteHTML(txt);
20554 // no firefox support
20555 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20556 this.execCmd('InsertHTML', txt);
20565 mozKeyPress : function(e){
20567 var c = e.getCharCode(), cmd;
20570 c = String.fromCharCode(c).toLowerCase();
20584 this.cleanUpPaste.defer(100, this);
20592 e.preventDefault();
20600 fixKeys : function(){ // load time branching for fastest keydown performance
20602 return function(e){
20603 var k = e.getKey(), r;
20606 r = this.doc.selection.createRange();
20609 r.pasteHTML('    ');
20616 r = this.doc.selection.createRange();
20618 var target = r.parentElement();
20619 if(!target || target.tagName.toLowerCase() != 'li'){
20621 r.pasteHTML('<br />');
20627 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20628 this.cleanUpPaste.defer(100, this);
20634 }else if(Roo.isOpera){
20635 return function(e){
20636 var k = e.getKey();
20640 this.execCmd('InsertHTML','    ');
20643 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20644 this.cleanUpPaste.defer(100, this);
20649 }else if(Roo.isSafari){
20650 return function(e){
20651 var k = e.getKey();
20655 this.execCmd('InsertText','\t');
20659 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20660 this.cleanUpPaste.defer(100, this);
20668 getAllAncestors: function()
20670 var p = this.getSelectedNode();
20673 a.push(p); // push blank onto stack..
20674 p = this.getParentElement();
20678 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20682 a.push(this.doc.body);
20686 lastSelNode : false,
20689 getSelection : function()
20691 this.assignDocWin();
20692 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20695 getSelectedNode: function()
20697 // this may only work on Gecko!!!
20699 // should we cache this!!!!
20704 var range = this.createRange(this.getSelection()).cloneRange();
20707 var parent = range.parentElement();
20709 var testRange = range.duplicate();
20710 testRange.moveToElementText(parent);
20711 if (testRange.inRange(range)) {
20714 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20717 parent = parent.parentElement;
20722 // is ancestor a text element.
20723 var ac = range.commonAncestorContainer;
20724 if (ac.nodeType == 3) {
20725 ac = ac.parentNode;
20728 var ar = ac.childNodes;
20731 var other_nodes = [];
20732 var has_other_nodes = false;
20733 for (var i=0;i<ar.length;i++) {
20734 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20737 // fullly contained node.
20739 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20744 // probably selected..
20745 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20746 other_nodes.push(ar[i]);
20750 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20755 has_other_nodes = true;
20757 if (!nodes.length && other_nodes.length) {
20758 nodes= other_nodes;
20760 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20766 createRange: function(sel)
20768 // this has strange effects when using with
20769 // top toolbar - not sure if it's a great idea.
20770 //this.editor.contentWindow.focus();
20771 if (typeof sel != "undefined") {
20773 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20775 return this.doc.createRange();
20778 return this.doc.createRange();
20781 getParentElement: function()
20784 this.assignDocWin();
20785 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20787 var range = this.createRange(sel);
20790 var p = range.commonAncestorContainer;
20791 while (p.nodeType == 3) { // text node
20802 * Range intersection.. the hard stuff...
20806 * [ -- selected range --- ]
20810 * if end is before start or hits it. fail.
20811 * if start is after end or hits it fail.
20813 * if either hits (but other is outside. - then it's not
20819 // @see http://www.thismuchiknow.co.uk/?p=64.
20820 rangeIntersectsNode : function(range, node)
20822 var nodeRange = node.ownerDocument.createRange();
20824 nodeRange.selectNode(node);
20826 nodeRange.selectNodeContents(node);
20829 var rangeStartRange = range.cloneRange();
20830 rangeStartRange.collapse(true);
20832 var rangeEndRange = range.cloneRange();
20833 rangeEndRange.collapse(false);
20835 var nodeStartRange = nodeRange.cloneRange();
20836 nodeStartRange.collapse(true);
20838 var nodeEndRange = nodeRange.cloneRange();
20839 nodeEndRange.collapse(false);
20841 return rangeStartRange.compareBoundaryPoints(
20842 Range.START_TO_START, nodeEndRange) == -1 &&
20843 rangeEndRange.compareBoundaryPoints(
20844 Range.START_TO_START, nodeStartRange) == 1;
20848 rangeCompareNode : function(range, node)
20850 var nodeRange = node.ownerDocument.createRange();
20852 nodeRange.selectNode(node);
20854 nodeRange.selectNodeContents(node);
20858 range.collapse(true);
20860 nodeRange.collapse(true);
20862 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20863 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20865 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20867 var nodeIsBefore = ss == 1;
20868 var nodeIsAfter = ee == -1;
20870 if (nodeIsBefore && nodeIsAfter) {
20873 if (!nodeIsBefore && nodeIsAfter) {
20874 return 1; //right trailed.
20877 if (nodeIsBefore && !nodeIsAfter) {
20878 return 2; // left trailed.
20884 // private? - in a new class?
20885 cleanUpPaste : function()
20887 // cleans up the whole document..
20888 Roo.log('cleanuppaste');
20890 this.cleanUpChildren(this.doc.body);
20891 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20892 if (clean != this.doc.body.innerHTML) {
20893 this.doc.body.innerHTML = clean;
20898 cleanWordChars : function(input) {// change the chars to hex code
20899 var he = Roo.HtmlEditorCore;
20901 var output = input;
20902 Roo.each(he.swapCodes, function(sw) {
20903 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20905 output = output.replace(swapper, sw[1]);
20912 cleanUpChildren : function (n)
20914 if (!n.childNodes.length) {
20917 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20918 this.cleanUpChild(n.childNodes[i]);
20925 cleanUpChild : function (node)
20928 //console.log(node);
20929 if (node.nodeName == "#text") {
20930 // clean up silly Windows -- stuff?
20933 if (node.nodeName == "#comment") {
20934 node.parentNode.removeChild(node);
20935 // clean up silly Windows -- stuff?
20938 var lcname = node.tagName.toLowerCase();
20939 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20940 // whitelist of tags..
20942 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20944 node.parentNode.removeChild(node);
20949 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20951 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20952 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20954 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20955 // remove_keep_children = true;
20958 if (remove_keep_children) {
20959 this.cleanUpChildren(node);
20960 // inserts everything just before this node...
20961 while (node.childNodes.length) {
20962 var cn = node.childNodes[0];
20963 node.removeChild(cn);
20964 node.parentNode.insertBefore(cn, node);
20966 node.parentNode.removeChild(node);
20970 if (!node.attributes || !node.attributes.length) {
20971 this.cleanUpChildren(node);
20975 function cleanAttr(n,v)
20978 if (v.match(/^\./) || v.match(/^\//)) {
20981 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20984 if (v.match(/^#/)) {
20987 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20988 node.removeAttribute(n);
20992 var cwhite = this.cwhite;
20993 var cblack = this.cblack;
20995 function cleanStyle(n,v)
20997 if (v.match(/expression/)) { //XSS?? should we even bother..
20998 node.removeAttribute(n);
21002 var parts = v.split(/;/);
21005 Roo.each(parts, function(p) {
21006 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21010 var l = p.split(':').shift().replace(/\s+/g,'');
21011 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21013 if ( cwhite.length && cblack.indexOf(l) > -1) {
21014 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21015 //node.removeAttribute(n);
21019 // only allow 'c whitelisted system attributes'
21020 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21021 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21022 //node.removeAttribute(n);
21032 if (clean.length) {
21033 node.setAttribute(n, clean.join(';'));
21035 node.removeAttribute(n);
21041 for (var i = node.attributes.length-1; i > -1 ; i--) {
21042 var a = node.attributes[i];
21045 if (a.name.toLowerCase().substr(0,2)=='on') {
21046 node.removeAttribute(a.name);
21049 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21050 node.removeAttribute(a.name);
21053 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21054 cleanAttr(a.name,a.value); // fixme..
21057 if (a.name == 'style') {
21058 cleanStyle(a.name,a.value);
21061 /// clean up MS crap..
21062 // tecnically this should be a list of valid class'es..
21065 if (a.name == 'class') {
21066 if (a.value.match(/^Mso/)) {
21067 node.className = '';
21070 if (a.value.match(/body/)) {
21071 node.className = '';
21082 this.cleanUpChildren(node);
21088 * Clean up MS wordisms...
21090 cleanWord : function(node)
21095 this.cleanWord(this.doc.body);
21098 if (node.nodeName == "#text") {
21099 // clean up silly Windows -- stuff?
21102 if (node.nodeName == "#comment") {
21103 node.parentNode.removeChild(node);
21104 // clean up silly Windows -- stuff?
21108 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21109 node.parentNode.removeChild(node);
21113 // remove - but keep children..
21114 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21115 while (node.childNodes.length) {
21116 var cn = node.childNodes[0];
21117 node.removeChild(cn);
21118 node.parentNode.insertBefore(cn, node);
21120 node.parentNode.removeChild(node);
21121 this.iterateChildren(node, this.cleanWord);
21125 if (node.className.length) {
21127 var cn = node.className.split(/\W+/);
21129 Roo.each(cn, function(cls) {
21130 if (cls.match(/Mso[a-zA-Z]+/)) {
21135 node.className = cna.length ? cna.join(' ') : '';
21137 node.removeAttribute("class");
21141 if (node.hasAttribute("lang")) {
21142 node.removeAttribute("lang");
21145 if (node.hasAttribute("style")) {
21147 var styles = node.getAttribute("style").split(";");
21149 Roo.each(styles, function(s) {
21150 if (!s.match(/:/)) {
21153 var kv = s.split(":");
21154 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21157 // what ever is left... we allow.
21160 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21161 if (!nstyle.length) {
21162 node.removeAttribute('style');
21165 this.iterateChildren(node, this.cleanWord);
21171 * iterateChildren of a Node, calling fn each time, using this as the scole..
21172 * @param {DomNode} node node to iterate children of.
21173 * @param {Function} fn method of this class to call on each item.
21175 iterateChildren : function(node, fn)
21177 if (!node.childNodes.length) {
21180 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21181 fn.call(this, node.childNodes[i])
21187 * cleanTableWidths.
21189 * Quite often pasting from word etc.. results in tables with column and widths.
21190 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21193 cleanTableWidths : function(node)
21198 this.cleanTableWidths(this.doc.body);
21203 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21206 Roo.log(node.tagName);
21207 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21208 this.iterateChildren(node, this.cleanTableWidths);
21211 if (node.hasAttribute('width')) {
21212 node.removeAttribute('width');
21216 if (node.hasAttribute("style")) {
21219 var styles = node.getAttribute("style").split(";");
21221 Roo.each(styles, function(s) {
21222 if (!s.match(/:/)) {
21225 var kv = s.split(":");
21226 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21229 // what ever is left... we allow.
21232 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21233 if (!nstyle.length) {
21234 node.removeAttribute('style');
21238 this.iterateChildren(node, this.cleanTableWidths);
21246 domToHTML : function(currentElement, depth, nopadtext) {
21248 depth = depth || 0;
21249 nopadtext = nopadtext || false;
21251 if (!currentElement) {
21252 return this.domToHTML(this.doc.body);
21255 //Roo.log(currentElement);
21257 var allText = false;
21258 var nodeName = currentElement.nodeName;
21259 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21261 if (nodeName == '#text') {
21263 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21268 if (nodeName != 'BODY') {
21271 // Prints the node tagName, such as <A>, <IMG>, etc
21274 for(i = 0; i < currentElement.attributes.length;i++) {
21276 var aname = currentElement.attributes.item(i).name;
21277 if (!currentElement.attributes.item(i).value.length) {
21280 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21283 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21292 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21295 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21300 // Traverse the tree
21302 var currentElementChild = currentElement.childNodes.item(i);
21303 var allText = true;
21304 var innerHTML = '';
21306 while (currentElementChild) {
21307 // Formatting code (indent the tree so it looks nice on the screen)
21308 var nopad = nopadtext;
21309 if (lastnode == 'SPAN') {
21313 if (currentElementChild.nodeName == '#text') {
21314 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21315 toadd = nopadtext ? toadd : toadd.trim();
21316 if (!nopad && toadd.length > 80) {
21317 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21319 innerHTML += toadd;
21322 currentElementChild = currentElement.childNodes.item(i);
21328 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21330 // Recursively traverse the tree structure of the child node
21331 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21332 lastnode = currentElementChild.nodeName;
21334 currentElementChild=currentElement.childNodes.item(i);
21340 // The remaining code is mostly for formatting the tree
21341 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21346 ret+= "</"+tagName+">";
21352 applyBlacklists : function()
21354 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21355 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21359 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21360 if (b.indexOf(tag) > -1) {
21363 this.white.push(tag);
21367 Roo.each(w, function(tag) {
21368 if (b.indexOf(tag) > -1) {
21371 if (this.white.indexOf(tag) > -1) {
21374 this.white.push(tag);
21379 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21380 if (w.indexOf(tag) > -1) {
21383 this.black.push(tag);
21387 Roo.each(b, function(tag) {
21388 if (w.indexOf(tag) > -1) {
21391 if (this.black.indexOf(tag) > -1) {
21394 this.black.push(tag);
21399 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21400 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21404 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21405 if (b.indexOf(tag) > -1) {
21408 this.cwhite.push(tag);
21412 Roo.each(w, function(tag) {
21413 if (b.indexOf(tag) > -1) {
21416 if (this.cwhite.indexOf(tag) > -1) {
21419 this.cwhite.push(tag);
21424 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21425 if (w.indexOf(tag) > -1) {
21428 this.cblack.push(tag);
21432 Roo.each(b, function(tag) {
21433 if (w.indexOf(tag) > -1) {
21436 if (this.cblack.indexOf(tag) > -1) {
21439 this.cblack.push(tag);
21444 setStylesheets : function(stylesheets)
21446 if(typeof(stylesheets) == 'string'){
21447 Roo.get(this.iframe.contentDocument.head).createChild({
21449 rel : 'stylesheet',
21458 Roo.each(stylesheets, function(s) {
21463 Roo.get(_this.iframe.contentDocument.head).createChild({
21465 rel : 'stylesheet',
21474 removeStylesheets : function()
21478 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21483 // hide stuff that is not compatible
21497 * @event specialkey
21501 * @cfg {String} fieldClass @hide
21504 * @cfg {String} focusClass @hide
21507 * @cfg {String} autoCreate @hide
21510 * @cfg {String} inputType @hide
21513 * @cfg {String} invalidClass @hide
21516 * @cfg {String} invalidText @hide
21519 * @cfg {String} msgFx @hide
21522 * @cfg {String} validateOnBlur @hide
21526 Roo.HtmlEditorCore.white = [
21527 'area', 'br', 'img', 'input', 'hr', 'wbr',
21529 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21530 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21531 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21532 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21533 'table', 'ul', 'xmp',
21535 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21538 'dir', 'menu', 'ol', 'ul', 'dl',
21544 Roo.HtmlEditorCore.black = [
21545 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21547 'base', 'basefont', 'bgsound', 'blink', 'body',
21548 'frame', 'frameset', 'head', 'html', 'ilayer',
21549 'iframe', 'layer', 'link', 'meta', 'object',
21550 'script', 'style' ,'title', 'xml' // clean later..
21552 Roo.HtmlEditorCore.clean = [
21553 'script', 'style', 'title', 'xml'
21555 Roo.HtmlEditorCore.remove = [
21560 Roo.HtmlEditorCore.ablack = [
21564 Roo.HtmlEditorCore.aclean = [
21565 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21569 Roo.HtmlEditorCore.pwhite= [
21570 'http', 'https', 'mailto'
21573 // white listed style attributes.
21574 Roo.HtmlEditorCore.cwhite= [
21575 // 'text-align', /// default is to allow most things..
21581 // black listed style attributes.
21582 Roo.HtmlEditorCore.cblack= [
21583 // 'font-size' -- this can be set by the project
21587 Roo.HtmlEditorCore.swapCodes =[
21606 * @class Roo.bootstrap.HtmlEditor
21607 * @extends Roo.bootstrap.TextArea
21608 * Bootstrap HtmlEditor class
21611 * Create a new HtmlEditor
21612 * @param {Object} config The config object
21615 Roo.bootstrap.HtmlEditor = function(config){
21616 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21617 if (!this.toolbars) {
21618 this.toolbars = [];
21620 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21623 * @event initialize
21624 * Fires when the editor is fully initialized (including the iframe)
21625 * @param {HtmlEditor} this
21630 * Fires when the editor is first receives the focus. Any insertion must wait
21631 * until after this event.
21632 * @param {HtmlEditor} this
21636 * @event beforesync
21637 * Fires before the textarea is updated with content from the editor iframe. Return false
21638 * to cancel the sync.
21639 * @param {HtmlEditor} this
21640 * @param {String} html
21644 * @event beforepush
21645 * Fires before the iframe editor is updated with content from the textarea. Return false
21646 * to cancel the push.
21647 * @param {HtmlEditor} this
21648 * @param {String} html
21653 * Fires when the textarea is updated with content from the editor iframe.
21654 * @param {HtmlEditor} this
21655 * @param {String} html
21660 * Fires when the iframe editor is updated with content from the textarea.
21661 * @param {HtmlEditor} this
21662 * @param {String} html
21666 * @event editmodechange
21667 * Fires when the editor switches edit modes
21668 * @param {HtmlEditor} this
21669 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21671 editmodechange: true,
21673 * @event editorevent
21674 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21675 * @param {HtmlEditor} this
21679 * @event firstfocus
21680 * Fires when on first focus - needed by toolbars..
21681 * @param {HtmlEditor} this
21686 * Auto save the htmlEditor value as a file into Events
21687 * @param {HtmlEditor} this
21691 * @event savedpreview
21692 * preview the saved version of htmlEditor
21693 * @param {HtmlEditor} this
21700 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21704 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21709 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21714 * @cfg {Number} height (in pixels)
21718 * @cfg {Number} width (in pixels)
21723 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21726 stylesheets: false,
21731 // private properties
21732 validationEvent : false,
21734 initialized : false,
21737 onFocus : Roo.emptyFn,
21739 hideMode:'offsets',
21742 tbContainer : false,
21744 toolbarContainer :function() {
21745 return this.wrap.select('.x-html-editor-tb',true).first();
21749 * Protected method that will not generally be called directly. It
21750 * is called when the editor creates its toolbar. Override this method if you need to
21751 * add custom toolbar buttons.
21752 * @param {HtmlEditor} editor
21754 createToolbar : function(){
21756 Roo.log("create toolbars");
21758 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21759 this.toolbars[0].render(this.toolbarContainer());
21763 // if (!editor.toolbars || !editor.toolbars.length) {
21764 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21767 // for (var i =0 ; i < editor.toolbars.length;i++) {
21768 // editor.toolbars[i] = Roo.factory(
21769 // typeof(editor.toolbars[i]) == 'string' ?
21770 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21771 // Roo.bootstrap.HtmlEditor);
21772 // editor.toolbars[i].init(editor);
21778 onRender : function(ct, position)
21780 // Roo.log("Call onRender: " + this.xtype);
21782 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21784 this.wrap = this.inputEl().wrap({
21785 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21788 this.editorcore.onRender(ct, position);
21790 if (this.resizable) {
21791 this.resizeEl = new Roo.Resizable(this.wrap, {
21795 minHeight : this.height,
21796 height: this.height,
21797 handles : this.resizable,
21800 resize : function(r, w, h) {
21801 _t.onResize(w,h); // -something
21807 this.createToolbar(this);
21810 if(!this.width && this.resizable){
21811 this.setSize(this.wrap.getSize());
21813 if (this.resizeEl) {
21814 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21815 // should trigger onReize..
21821 onResize : function(w, h)
21823 Roo.log('resize: ' +w + ',' + h );
21824 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21828 if(this.inputEl() ){
21829 if(typeof w == 'number'){
21830 var aw = w - this.wrap.getFrameWidth('lr');
21831 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21834 if(typeof h == 'number'){
21835 var tbh = -11; // fixme it needs to tool bar size!
21836 for (var i =0; i < this.toolbars.length;i++) {
21837 // fixme - ask toolbars for heights?
21838 tbh += this.toolbars[i].el.getHeight();
21839 //if (this.toolbars[i].footer) {
21840 // tbh += this.toolbars[i].footer.el.getHeight();
21848 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21849 ah -= 5; // knock a few pixes off for look..
21850 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21854 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21855 this.editorcore.onResize(ew,eh);
21860 * Toggles the editor between standard and source edit mode.
21861 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21863 toggleSourceEdit : function(sourceEditMode)
21865 this.editorcore.toggleSourceEdit(sourceEditMode);
21867 if(this.editorcore.sourceEditMode){
21868 Roo.log('editor - showing textarea');
21871 // Roo.log(this.syncValue());
21873 this.inputEl().removeClass(['hide', 'x-hidden']);
21874 this.inputEl().dom.removeAttribute('tabIndex');
21875 this.inputEl().focus();
21877 Roo.log('editor - hiding textarea');
21879 // Roo.log(this.pushValue());
21882 this.inputEl().addClass(['hide', 'x-hidden']);
21883 this.inputEl().dom.setAttribute('tabIndex', -1);
21884 //this.deferFocus();
21887 if(this.resizable){
21888 this.setSize(this.wrap.getSize());
21891 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21894 // private (for BoxComponent)
21895 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21897 // private (for BoxComponent)
21898 getResizeEl : function(){
21902 // private (for BoxComponent)
21903 getPositionEl : function(){
21908 initEvents : function(){
21909 this.originalValue = this.getValue();
21913 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21916 // markInvalid : Roo.emptyFn,
21918 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21921 // clearInvalid : Roo.emptyFn,
21923 setValue : function(v){
21924 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21925 this.editorcore.pushValue();
21930 deferFocus : function(){
21931 this.focus.defer(10, this);
21935 focus : function(){
21936 this.editorcore.focus();
21942 onDestroy : function(){
21948 for (var i =0; i < this.toolbars.length;i++) {
21949 // fixme - ask toolbars for heights?
21950 this.toolbars[i].onDestroy();
21953 this.wrap.dom.innerHTML = '';
21954 this.wrap.remove();
21959 onFirstFocus : function(){
21960 //Roo.log("onFirstFocus");
21961 this.editorcore.onFirstFocus();
21962 for (var i =0; i < this.toolbars.length;i++) {
21963 this.toolbars[i].onFirstFocus();
21969 syncValue : function()
21971 this.editorcore.syncValue();
21974 pushValue : function()
21976 this.editorcore.pushValue();
21980 // hide stuff that is not compatible
21994 * @event specialkey
21998 * @cfg {String} fieldClass @hide
22001 * @cfg {String} focusClass @hide
22004 * @cfg {String} autoCreate @hide
22007 * @cfg {String} inputType @hide
22010 * @cfg {String} invalidClass @hide
22013 * @cfg {String} invalidText @hide
22016 * @cfg {String} msgFx @hide
22019 * @cfg {String} validateOnBlur @hide
22028 Roo.namespace('Roo.bootstrap.htmleditor');
22030 * @class Roo.bootstrap.HtmlEditorToolbar1
22035 new Roo.bootstrap.HtmlEditor({
22038 new Roo.bootstrap.HtmlEditorToolbar1({
22039 disable : { fonts: 1 , format: 1, ..., ... , ...],
22045 * @cfg {Object} disable List of elements to disable..
22046 * @cfg {Array} btns List of additional buttons.
22050 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22053 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22056 Roo.apply(this, config);
22058 // default disabled, based on 'good practice'..
22059 this.disable = this.disable || {};
22060 Roo.applyIf(this.disable, {
22063 specialElements : true
22065 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22067 this.editor = config.editor;
22068 this.editorcore = config.editor.editorcore;
22070 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22072 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22073 // dont call parent... till later.
22075 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22080 editorcore : false,
22085 "h1","h2","h3","h4","h5","h6",
22087 "abbr", "acronym", "address", "cite", "samp", "var",
22091 onRender : function(ct, position)
22093 // Roo.log("Call onRender: " + this.xtype);
22095 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22097 this.el.dom.style.marginBottom = '0';
22099 var editorcore = this.editorcore;
22100 var editor= this.editor;
22103 var btn = function(id,cmd , toggle, handler){
22105 var event = toggle ? 'toggle' : 'click';
22110 xns: Roo.bootstrap,
22113 enableToggle:toggle !== false,
22115 pressed : toggle ? false : null,
22118 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22119 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22128 xns: Roo.bootstrap,
22129 glyphicon : 'font',
22133 xns: Roo.bootstrap,
22137 Roo.each(this.formats, function(f) {
22138 style.menu.items.push({
22140 xns: Roo.bootstrap,
22141 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22146 editorcore.insertTag(this.tagname);
22153 children.push(style);
22156 btn('bold',false,true);
22157 btn('italic',false,true);
22158 btn('align-left', 'justifyleft',true);
22159 btn('align-center', 'justifycenter',true);
22160 btn('align-right' , 'justifyright',true);
22161 btn('link', false, false, function(btn) {
22162 //Roo.log("create link?");
22163 var url = prompt(this.createLinkText, this.defaultLinkValue);
22164 if(url && url != 'http:/'+'/'){
22165 this.editorcore.relayCmd('createlink', url);
22168 btn('list','insertunorderedlist',true);
22169 btn('pencil', false,true, function(btn){
22172 this.toggleSourceEdit(btn.pressed);
22178 xns: Roo.bootstrap,
22183 xns: Roo.bootstrap,
22188 cog.menu.items.push({
22190 xns: Roo.bootstrap,
22191 html : Clean styles,
22196 editorcore.insertTag(this.tagname);
22205 this.xtype = 'NavSimplebar';
22207 for(var i=0;i< children.length;i++) {
22209 this.buttons.add(this.addxtypeChild(children[i]));
22213 editor.on('editorevent', this.updateToolbar, this);
22215 onBtnClick : function(id)
22217 this.editorcore.relayCmd(id);
22218 this.editorcore.focus();
22222 * Protected method that will not generally be called directly. It triggers
22223 * a toolbar update by reading the markup state of the current selection in the editor.
22225 updateToolbar: function(){
22227 if(!this.editorcore.activated){
22228 this.editor.onFirstFocus(); // is this neeed?
22232 var btns = this.buttons;
22233 var doc = this.editorcore.doc;
22234 btns.get('bold').setActive(doc.queryCommandState('bold'));
22235 btns.get('italic').setActive(doc.queryCommandState('italic'));
22236 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22238 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22239 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22240 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22242 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22243 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22246 var ans = this.editorcore.getAllAncestors();
22247 if (this.formatCombo) {
22250 var store = this.formatCombo.store;
22251 this.formatCombo.setValue("");
22252 for (var i =0; i < ans.length;i++) {
22253 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22255 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22263 // hides menus... - so this cant be on a menu...
22264 Roo.bootstrap.MenuMgr.hideAll();
22266 Roo.bootstrap.MenuMgr.hideAll();
22267 //this.editorsyncValue();
22269 onFirstFocus: function() {
22270 this.buttons.each(function(item){
22274 toggleSourceEdit : function(sourceEditMode){
22277 if(sourceEditMode){
22278 Roo.log("disabling buttons");
22279 this.buttons.each( function(item){
22280 if(item.cmd != 'pencil'){
22286 Roo.log("enabling buttons");
22287 if(this.editorcore.initialized){
22288 this.buttons.each( function(item){
22294 Roo.log("calling toggole on editor");
22295 // tell the editor that it's been pressed..
22296 this.editor.toggleSourceEdit(sourceEditMode);
22306 * @class Roo.bootstrap.Table.AbstractSelectionModel
22307 * @extends Roo.util.Observable
22308 * Abstract base class for grid SelectionModels. It provides the interface that should be
22309 * implemented by descendant classes. This class should not be directly instantiated.
22312 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22313 this.locked = false;
22314 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22318 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22319 /** @ignore Called by the grid automatically. Do not call directly. */
22320 init : function(grid){
22326 * Locks the selections.
22329 this.locked = true;
22333 * Unlocks the selections.
22335 unlock : function(){
22336 this.locked = false;
22340 * Returns true if the selections are locked.
22341 * @return {Boolean}
22343 isLocked : function(){
22344 return this.locked;
22348 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22349 * @class Roo.bootstrap.Table.RowSelectionModel
22350 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22351 * It supports multiple selections and keyboard selection/navigation.
22353 * @param {Object} config
22356 Roo.bootstrap.Table.RowSelectionModel = function(config){
22357 Roo.apply(this, config);
22358 this.selections = new Roo.util.MixedCollection(false, function(o){
22363 this.lastActive = false;
22367 * @event selectionchange
22368 * Fires when the selection changes
22369 * @param {SelectionModel} this
22371 "selectionchange" : true,
22373 * @event afterselectionchange
22374 * Fires after the selection changes (eg. by key press or clicking)
22375 * @param {SelectionModel} this
22377 "afterselectionchange" : true,
22379 * @event beforerowselect
22380 * Fires when a row is selected being selected, return false to cancel.
22381 * @param {SelectionModel} this
22382 * @param {Number} rowIndex The selected index
22383 * @param {Boolean} keepExisting False if other selections will be cleared
22385 "beforerowselect" : true,
22388 * Fires when a row is selected.
22389 * @param {SelectionModel} this
22390 * @param {Number} rowIndex The selected index
22391 * @param {Roo.data.Record} r The record
22393 "rowselect" : true,
22395 * @event rowdeselect
22396 * Fires when a row is deselected.
22397 * @param {SelectionModel} this
22398 * @param {Number} rowIndex The selected index
22400 "rowdeselect" : true
22402 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22403 this.locked = false;
22406 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22408 * @cfg {Boolean} singleSelect
22409 * True to allow selection of only one row at a time (defaults to false)
22411 singleSelect : false,
22414 initEvents : function(){
22416 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22417 this.grid.on("mousedown", this.handleMouseDown, this);
22418 }else{ // allow click to work like normal
22419 this.grid.on("rowclick", this.handleDragableRowClick, this);
22422 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22423 "up" : function(e){
22425 this.selectPrevious(e.shiftKey);
22426 }else if(this.last !== false && this.lastActive !== false){
22427 var last = this.last;
22428 this.selectRange(this.last, this.lastActive-1);
22429 this.grid.getView().focusRow(this.lastActive);
22430 if(last !== false){
22434 this.selectFirstRow();
22436 this.fireEvent("afterselectionchange", this);
22438 "down" : function(e){
22440 this.selectNext(e.shiftKey);
22441 }else if(this.last !== false && this.lastActive !== false){
22442 var last = this.last;
22443 this.selectRange(this.last, this.lastActive+1);
22444 this.grid.getView().focusRow(this.lastActive);
22445 if(last !== false){
22449 this.selectFirstRow();
22451 this.fireEvent("afterselectionchange", this);
22456 var view = this.grid.view;
22457 view.on("refresh", this.onRefresh, this);
22458 view.on("rowupdated", this.onRowUpdated, this);
22459 view.on("rowremoved", this.onRemove, this);
22463 onRefresh : function(){
22464 var ds = this.grid.dataSource, i, v = this.grid.view;
22465 var s = this.selections;
22466 s.each(function(r){
22467 if((i = ds.indexOfId(r.id)) != -1){
22476 onRemove : function(v, index, r){
22477 this.selections.remove(r);
22481 onRowUpdated : function(v, index, r){
22482 if(this.isSelected(r)){
22483 v.onRowSelect(index);
22489 * @param {Array} records The records to select
22490 * @param {Boolean} keepExisting (optional) True to keep existing selections
22492 selectRecords : function(records, keepExisting){
22494 this.clearSelections();
22496 var ds = this.grid.dataSource;
22497 for(var i = 0, len = records.length; i < len; i++){
22498 this.selectRow(ds.indexOf(records[i]), true);
22503 * Gets the number of selected rows.
22506 getCount : function(){
22507 return this.selections.length;
22511 * Selects the first row in the grid.
22513 selectFirstRow : function(){
22518 * Select the last row.
22519 * @param {Boolean} keepExisting (optional) True to keep existing selections
22521 selectLastRow : function(keepExisting){
22522 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22526 * Selects the row immediately following the last selected row.
22527 * @param {Boolean} keepExisting (optional) True to keep existing selections
22529 selectNext : function(keepExisting){
22530 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22531 this.selectRow(this.last+1, keepExisting);
22532 this.grid.getView().focusRow(this.last);
22537 * Selects the row that precedes the last selected row.
22538 * @param {Boolean} keepExisting (optional) True to keep existing selections
22540 selectPrevious : function(keepExisting){
22542 this.selectRow(this.last-1, keepExisting);
22543 this.grid.getView().focusRow(this.last);
22548 * Returns the selected records
22549 * @return {Array} Array of selected records
22551 getSelections : function(){
22552 return [].concat(this.selections.items);
22556 * Returns the first selected record.
22559 getSelected : function(){
22560 return this.selections.itemAt(0);
22565 * Clears all selections.
22567 clearSelections : function(fast){
22572 var ds = this.grid.dataSource;
22573 var s = this.selections;
22574 s.each(function(r){
22575 this.deselectRow(ds.indexOfId(r.id));
22579 this.selections.clear();
22586 * Selects all rows.
22588 selectAll : function(){
22592 this.selections.clear();
22593 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22594 this.selectRow(i, true);
22599 * Returns True if there is a selection.
22600 * @return {Boolean}
22602 hasSelection : function(){
22603 return this.selections.length > 0;
22607 * Returns True if the specified row is selected.
22608 * @param {Number/Record} record The record or index of the record to check
22609 * @return {Boolean}
22611 isSelected : function(index){
22612 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22613 return (r && this.selections.key(r.id) ? true : false);
22617 * Returns True if the specified record id is selected.
22618 * @param {String} id The id of record to check
22619 * @return {Boolean}
22621 isIdSelected : function(id){
22622 return (this.selections.key(id) ? true : false);
22626 handleMouseDown : function(e, t){
22627 var view = this.grid.getView(), rowIndex;
22628 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22631 if(e.shiftKey && this.last !== false){
22632 var last = this.last;
22633 this.selectRange(last, rowIndex, e.ctrlKey);
22634 this.last = last; // reset the last
22635 view.focusRow(rowIndex);
22637 var isSelected = this.isSelected(rowIndex);
22638 if(e.button !== 0 && isSelected){
22639 view.focusRow(rowIndex);
22640 }else if(e.ctrlKey && isSelected){
22641 this.deselectRow(rowIndex);
22642 }else if(!isSelected){
22643 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22644 view.focusRow(rowIndex);
22647 this.fireEvent("afterselectionchange", this);
22650 handleDragableRowClick : function(grid, rowIndex, e)
22652 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22653 this.selectRow(rowIndex, false);
22654 grid.view.focusRow(rowIndex);
22655 this.fireEvent("afterselectionchange", this);
22660 * Selects multiple rows.
22661 * @param {Array} rows Array of the indexes of the row to select
22662 * @param {Boolean} keepExisting (optional) True to keep existing selections
22664 selectRows : function(rows, keepExisting){
22666 this.clearSelections();
22668 for(var i = 0, len = rows.length; i < len; i++){
22669 this.selectRow(rows[i], true);
22674 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22675 * @param {Number} startRow The index of the first row in the range
22676 * @param {Number} endRow The index of the last row in the range
22677 * @param {Boolean} keepExisting (optional) True to retain existing selections
22679 selectRange : function(startRow, endRow, keepExisting){
22684 this.clearSelections();
22686 if(startRow <= endRow){
22687 for(var i = startRow; i <= endRow; i++){
22688 this.selectRow(i, true);
22691 for(var i = startRow; i >= endRow; i--){
22692 this.selectRow(i, true);
22698 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22699 * @param {Number} startRow The index of the first row in the range
22700 * @param {Number} endRow The index of the last row in the range
22702 deselectRange : function(startRow, endRow, preventViewNotify){
22706 for(var i = startRow; i <= endRow; i++){
22707 this.deselectRow(i, preventViewNotify);
22713 * @param {Number} row The index of the row to select
22714 * @param {Boolean} keepExisting (optional) True to keep existing selections
22716 selectRow : function(index, keepExisting, preventViewNotify){
22717 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22720 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22721 if(!keepExisting || this.singleSelect){
22722 this.clearSelections();
22724 var r = this.grid.dataSource.getAt(index);
22725 this.selections.add(r);
22726 this.last = this.lastActive = index;
22727 if(!preventViewNotify){
22728 this.grid.getView().onRowSelect(index);
22730 this.fireEvent("rowselect", this, index, r);
22731 this.fireEvent("selectionchange", this);
22737 * @param {Number} row The index of the row to deselect
22739 deselectRow : function(index, preventViewNotify){
22743 if(this.last == index){
22746 if(this.lastActive == index){
22747 this.lastActive = false;
22749 var r = this.grid.dataSource.getAt(index);
22750 this.selections.remove(r);
22751 if(!preventViewNotify){
22752 this.grid.getView().onRowDeselect(index);
22754 this.fireEvent("rowdeselect", this, index);
22755 this.fireEvent("selectionchange", this);
22759 restoreLast : function(){
22761 this.last = this._last;
22766 acceptsNav : function(row, col, cm){
22767 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22771 onEditorKey : function(field, e){
22772 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22777 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22779 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22781 }else if(k == e.ENTER && !e.ctrlKey){
22785 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22787 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22789 }else if(k == e.ESC){
22793 g.startEditing(newCell[0], newCell[1]);
22798 * Ext JS Library 1.1.1
22799 * Copyright(c) 2006-2007, Ext JS, LLC.
22801 * Originally Released Under LGPL - original licence link has changed is not relivant.
22804 * <script type="text/javascript">
22808 * @class Roo.bootstrap.PagingToolbar
22809 * @extends Roo.bootstrap.NavSimplebar
22810 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22812 * Create a new PagingToolbar
22813 * @param {Object} config The config object
22814 * @param {Roo.data.Store} store
22816 Roo.bootstrap.PagingToolbar = function(config)
22818 // old args format still supported... - xtype is prefered..
22819 // created from xtype...
22821 this.ds = config.dataSource;
22823 if (config.store && !this.ds) {
22824 this.store= Roo.factory(config.store, Roo.data);
22825 this.ds = this.store;
22826 this.ds.xmodule = this.xmodule || false;
22829 this.toolbarItems = [];
22830 if (config.items) {
22831 this.toolbarItems = config.items;
22834 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22839 this.bind(this.ds);
22842 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22846 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22848 * @cfg {Roo.data.Store} dataSource
22849 * The underlying data store providing the paged data
22852 * @cfg {String/HTMLElement/Element} container
22853 * container The id or element that will contain the toolbar
22856 * @cfg {Boolean} displayInfo
22857 * True to display the displayMsg (defaults to false)
22860 * @cfg {Number} pageSize
22861 * The number of records to display per page (defaults to 20)
22865 * @cfg {String} displayMsg
22866 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22868 displayMsg : 'Displaying {0} - {1} of {2}',
22870 * @cfg {String} emptyMsg
22871 * The message to display when no records are found (defaults to "No data to display")
22873 emptyMsg : 'No data to display',
22875 * Customizable piece of the default paging text (defaults to "Page")
22878 beforePageText : "Page",
22880 * Customizable piece of the default paging text (defaults to "of %0")
22883 afterPageText : "of {0}",
22885 * Customizable piece of the default paging text (defaults to "First Page")
22888 firstText : "First Page",
22890 * Customizable piece of the default paging text (defaults to "Previous Page")
22893 prevText : "Previous Page",
22895 * Customizable piece of the default paging text (defaults to "Next Page")
22898 nextText : "Next Page",
22900 * Customizable piece of the default paging text (defaults to "Last Page")
22903 lastText : "Last Page",
22905 * Customizable piece of the default paging text (defaults to "Refresh")
22908 refreshText : "Refresh",
22912 onRender : function(ct, position)
22914 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22915 this.navgroup.parentId = this.id;
22916 this.navgroup.onRender(this.el, null);
22917 // add the buttons to the navgroup
22919 if(this.displayInfo){
22920 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22921 this.displayEl = this.el.select('.x-paging-info', true).first();
22922 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22923 // this.displayEl = navel.el.select('span',true).first();
22929 Roo.each(_this.buttons, function(e){ // this might need to use render????
22930 Roo.factory(e).onRender(_this.el, null);
22934 Roo.each(_this.toolbarItems, function(e) {
22935 _this.navgroup.addItem(e);
22939 this.first = this.navgroup.addItem({
22940 tooltip: this.firstText,
22942 icon : 'fa fa-backward',
22944 preventDefault: true,
22945 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22948 this.prev = this.navgroup.addItem({
22949 tooltip: this.prevText,
22951 icon : 'fa fa-step-backward',
22953 preventDefault: true,
22954 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22956 //this.addSeparator();
22959 var field = this.navgroup.addItem( {
22961 cls : 'x-paging-position',
22963 html : this.beforePageText +
22964 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22965 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22968 this.field = field.el.select('input', true).first();
22969 this.field.on("keydown", this.onPagingKeydown, this);
22970 this.field.on("focus", function(){this.dom.select();});
22973 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22974 //this.field.setHeight(18);
22975 //this.addSeparator();
22976 this.next = this.navgroup.addItem({
22977 tooltip: this.nextText,
22979 html : ' <i class="fa fa-step-forward">',
22981 preventDefault: true,
22982 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22984 this.last = this.navgroup.addItem({
22985 tooltip: this.lastText,
22986 icon : 'fa fa-forward',
22989 preventDefault: true,
22990 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22992 //this.addSeparator();
22993 this.loading = this.navgroup.addItem({
22994 tooltip: this.refreshText,
22995 icon: 'fa fa-refresh',
22996 preventDefault: true,
22997 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23003 updateInfo : function(){
23004 if(this.displayEl){
23005 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23006 var msg = count == 0 ?
23010 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23012 this.displayEl.update(msg);
23017 onLoad : function(ds, r, o){
23018 this.cursor = o.params ? o.params.start : 0;
23019 var d = this.getPageData(),
23023 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23024 this.field.dom.value = ap;
23025 this.first.setDisabled(ap == 1);
23026 this.prev.setDisabled(ap == 1);
23027 this.next.setDisabled(ap == ps);
23028 this.last.setDisabled(ap == ps);
23029 this.loading.enable();
23034 getPageData : function(){
23035 var total = this.ds.getTotalCount();
23038 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23039 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23044 onLoadError : function(){
23045 this.loading.enable();
23049 onPagingKeydown : function(e){
23050 var k = e.getKey();
23051 var d = this.getPageData();
23053 var v = this.field.dom.value, pageNum;
23054 if(!v || isNaN(pageNum = parseInt(v, 10))){
23055 this.field.dom.value = d.activePage;
23058 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23059 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23062 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))
23064 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23065 this.field.dom.value = pageNum;
23066 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23069 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23071 var v = this.field.dom.value, pageNum;
23072 var increment = (e.shiftKey) ? 10 : 1;
23073 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23076 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23077 this.field.dom.value = d.activePage;
23080 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23082 this.field.dom.value = parseInt(v, 10) + increment;
23083 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23084 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23091 beforeLoad : function(){
23093 this.loading.disable();
23098 onClick : function(which){
23107 ds.load({params:{start: 0, limit: this.pageSize}});
23110 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23113 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23116 var total = ds.getTotalCount();
23117 var extra = total % this.pageSize;
23118 var lastStart = extra ? (total - extra) : total-this.pageSize;
23119 ds.load({params:{start: lastStart, limit: this.pageSize}});
23122 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23128 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23129 * @param {Roo.data.Store} store The data store to unbind
23131 unbind : function(ds){
23132 ds.un("beforeload", this.beforeLoad, this);
23133 ds.un("load", this.onLoad, this);
23134 ds.un("loadexception", this.onLoadError, this);
23135 ds.un("remove", this.updateInfo, this);
23136 ds.un("add", this.updateInfo, this);
23137 this.ds = undefined;
23141 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23142 * @param {Roo.data.Store} store The data store to bind
23144 bind : function(ds){
23145 ds.on("beforeload", this.beforeLoad, this);
23146 ds.on("load", this.onLoad, this);
23147 ds.on("loadexception", this.onLoadError, this);
23148 ds.on("remove", this.updateInfo, this);
23149 ds.on("add", this.updateInfo, this);
23160 * @class Roo.bootstrap.MessageBar
23161 * @extends Roo.bootstrap.Component
23162 * Bootstrap MessageBar class
23163 * @cfg {String} html contents of the MessageBar
23164 * @cfg {String} weight (info | success | warning | danger) default info
23165 * @cfg {String} beforeClass insert the bar before the given class
23166 * @cfg {Boolean} closable (true | false) default false
23167 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23170 * Create a new Element
23171 * @param {Object} config The config object
23174 Roo.bootstrap.MessageBar = function(config){
23175 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23178 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23184 beforeClass: 'bootstrap-sticky-wrap',
23186 getAutoCreate : function(){
23190 cls: 'alert alert-dismissable alert-' + this.weight,
23195 html: this.html || ''
23201 cfg.cls += ' alert-messages-fixed';
23215 onRender : function(ct, position)
23217 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23220 var cfg = Roo.apply({}, this.getAutoCreate());
23224 cfg.cls += ' ' + this.cls;
23227 cfg.style = this.style;
23229 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23231 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23234 this.el.select('>button.close').on('click', this.hide, this);
23240 if (!this.rendered) {
23246 this.fireEvent('show', this);
23252 if (!this.rendered) {
23258 this.fireEvent('hide', this);
23261 update : function()
23263 // var e = this.el.dom.firstChild;
23265 // if(this.closable){
23266 // e = e.nextSibling;
23269 // e.data = this.html || '';
23271 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23287 * @class Roo.bootstrap.Graph
23288 * @extends Roo.bootstrap.Component
23289 * Bootstrap Graph class
23293 @cfg {String} graphtype bar | vbar | pie
23294 @cfg {number} g_x coodinator | centre x (pie)
23295 @cfg {number} g_y coodinator | centre y (pie)
23296 @cfg {number} g_r radius (pie)
23297 @cfg {number} g_height height of the chart (respected by all elements in the set)
23298 @cfg {number} g_width width of the chart (respected by all elements in the set)
23299 @cfg {Object} title The title of the chart
23302 -opts (object) options for the chart
23304 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23305 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23307 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.
23308 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23310 o stretch (boolean)
23312 -opts (object) options for the pie
23315 o startAngle (number)
23316 o endAngle (number)
23320 * Create a new Input
23321 * @param {Object} config The config object
23324 Roo.bootstrap.Graph = function(config){
23325 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23331 * The img click event for the img.
23332 * @param {Roo.EventObject} e
23338 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23349 //g_colors: this.colors,
23356 getAutoCreate : function(){
23367 onRender : function(ct,position){
23370 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23372 if (typeof(Raphael) == 'undefined') {
23373 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23377 this.raphael = Raphael(this.el.dom);
23379 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23380 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23381 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23382 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23384 r.text(160, 10, "Single Series Chart").attr(txtattr);
23385 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23386 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23387 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23389 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23390 r.barchart(330, 10, 300, 220, data1);
23391 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23392 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23395 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23396 // r.barchart(30, 30, 560, 250, xdata, {
23397 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23398 // axis : "0 0 1 1",
23399 // axisxlabels : xdata
23400 // //yvalues : cols,
23403 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23405 // this.load(null,xdata,{
23406 // axis : "0 0 1 1",
23407 // axisxlabels : xdata
23412 load : function(graphtype,xdata,opts)
23414 this.raphael.clear();
23416 graphtype = this.graphtype;
23421 var r = this.raphael,
23422 fin = function () {
23423 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23425 fout = function () {
23426 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23428 pfin = function() {
23429 this.sector.stop();
23430 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23433 this.label[0].stop();
23434 this.label[0].attr({ r: 7.5 });
23435 this.label[1].attr({ "font-weight": 800 });
23438 pfout = function() {
23439 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23442 this.label[0].animate({ r: 5 }, 500, "bounce");
23443 this.label[1].attr({ "font-weight": 400 });
23449 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23452 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23455 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23456 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23458 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23465 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23470 setTitle: function(o)
23475 initEvents: function() {
23478 this.el.on('click', this.onClick, this);
23482 onClick : function(e)
23484 Roo.log('img onclick');
23485 this.fireEvent('click', this, e);
23497 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23500 * @class Roo.bootstrap.dash.NumberBox
23501 * @extends Roo.bootstrap.Component
23502 * Bootstrap NumberBox class
23503 * @cfg {String} headline Box headline
23504 * @cfg {String} content Box content
23505 * @cfg {String} icon Box icon
23506 * @cfg {String} footer Footer text
23507 * @cfg {String} fhref Footer href
23510 * Create a new NumberBox
23511 * @param {Object} config The config object
23515 Roo.bootstrap.dash.NumberBox = function(config){
23516 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23520 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23529 getAutoCreate : function(){
23533 cls : 'small-box ',
23541 cls : 'roo-headline',
23542 html : this.headline
23546 cls : 'roo-content',
23547 html : this.content
23561 cls : 'ion ' + this.icon
23570 cls : 'small-box-footer',
23571 href : this.fhref || '#',
23575 cfg.cn.push(footer);
23582 onRender : function(ct,position){
23583 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23590 setHeadline: function (value)
23592 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23595 setFooter: function (value, href)
23597 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23600 this.el.select('a.small-box-footer',true).first().attr('href', href);
23605 setContent: function (value)
23607 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23610 initEvents: function()
23624 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23627 * @class Roo.bootstrap.dash.TabBox
23628 * @extends Roo.bootstrap.Component
23629 * Bootstrap TabBox class
23630 * @cfg {String} title Title of the TabBox
23631 * @cfg {String} icon Icon of the TabBox
23632 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23633 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23636 * Create a new TabBox
23637 * @param {Object} config The config object
23641 Roo.bootstrap.dash.TabBox = function(config){
23642 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23647 * When a pane is added
23648 * @param {Roo.bootstrap.dash.TabPane} pane
23652 * @event activatepane
23653 * When a pane is activated
23654 * @param {Roo.bootstrap.dash.TabPane} pane
23656 "activatepane" : true
23664 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23669 tabScrollable : false,
23671 getChildContainer : function()
23673 return this.el.select('.tab-content', true).first();
23676 getAutoCreate : function(){
23680 cls: 'pull-left header',
23688 cls: 'fa ' + this.icon
23694 cls: 'nav nav-tabs pull-right',
23700 if(this.tabScrollable){
23707 cls: 'nav nav-tabs pull-right',
23718 cls: 'nav-tabs-custom',
23723 cls: 'tab-content no-padding',
23731 initEvents : function()
23733 //Roo.log('add add pane handler');
23734 this.on('addpane', this.onAddPane, this);
23737 * Updates the box title
23738 * @param {String} html to set the title to.
23740 setTitle : function(value)
23742 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23744 onAddPane : function(pane)
23746 this.panes.push(pane);
23747 //Roo.log('addpane');
23749 // tabs are rendere left to right..
23750 if(!this.showtabs){
23754 var ctr = this.el.select('.nav-tabs', true).first();
23757 var existing = ctr.select('.nav-tab',true);
23758 var qty = existing.getCount();;
23761 var tab = ctr.createChild({
23763 cls : 'nav-tab' + (qty ? '' : ' active'),
23771 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23774 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23776 pane.el.addClass('active');
23781 onTabClick : function(ev,un,ob,pane)
23783 //Roo.log('tab - prev default');
23784 ev.preventDefault();
23787 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23788 pane.tab.addClass('active');
23789 //Roo.log(pane.title);
23790 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23791 // technically we should have a deactivate event.. but maybe add later.
23792 // and it should not de-activate the selected tab...
23793 this.fireEvent('activatepane', pane);
23794 pane.el.addClass('active');
23795 pane.fireEvent('activate');
23800 getActivePane : function()
23803 Roo.each(this.panes, function(p) {
23804 if(p.el.hasClass('active')){
23825 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23827 * @class Roo.bootstrap.TabPane
23828 * @extends Roo.bootstrap.Component
23829 * Bootstrap TabPane class
23830 * @cfg {Boolean} active (false | true) Default false
23831 * @cfg {String} title title of panel
23835 * Create a new TabPane
23836 * @param {Object} config The config object
23839 Roo.bootstrap.dash.TabPane = function(config){
23840 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23846 * When a pane is activated
23847 * @param {Roo.bootstrap.dash.TabPane} pane
23854 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23859 // the tabBox that this is attached to.
23862 getAutoCreate : function()
23870 cfg.cls += ' active';
23875 initEvents : function()
23877 //Roo.log('trigger add pane handler');
23878 this.parent().fireEvent('addpane', this)
23882 * Updates the tab title
23883 * @param {String} html to set the title to.
23885 setTitle: function(str)
23891 this.tab.select('a', true).first().dom.innerHTML = str;
23908 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23911 * @class Roo.bootstrap.menu.Menu
23912 * @extends Roo.bootstrap.Component
23913 * Bootstrap Menu class - container for Menu
23914 * @cfg {String} html Text of the menu
23915 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23916 * @cfg {String} icon Font awesome icon
23917 * @cfg {String} pos Menu align to (top | bottom) default bottom
23921 * Create a new Menu
23922 * @param {Object} config The config object
23926 Roo.bootstrap.menu.Menu = function(config){
23927 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23931 * @event beforeshow
23932 * Fires before this menu is displayed
23933 * @param {Roo.bootstrap.menu.Menu} this
23937 * @event beforehide
23938 * Fires before this menu is hidden
23939 * @param {Roo.bootstrap.menu.Menu} this
23944 * Fires after this menu is displayed
23945 * @param {Roo.bootstrap.menu.Menu} this
23950 * Fires after this menu is hidden
23951 * @param {Roo.bootstrap.menu.Menu} this
23956 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23957 * @param {Roo.bootstrap.menu.Menu} this
23958 * @param {Roo.EventObject} e
23965 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23969 weight : 'default',
23974 getChildContainer : function() {
23975 if(this.isSubMenu){
23979 return this.el.select('ul.dropdown-menu', true).first();
23982 getAutoCreate : function()
23987 cls : 'roo-menu-text',
23995 cls : 'fa ' + this.icon
24006 cls : 'dropdown-button btn btn-' + this.weight,
24011 cls : 'dropdown-toggle btn btn-' + this.weight,
24021 cls : 'dropdown-menu'
24027 if(this.pos == 'top'){
24028 cfg.cls += ' dropup';
24031 if(this.isSubMenu){
24034 cls : 'dropdown-menu'
24041 onRender : function(ct, position)
24043 this.isSubMenu = ct.hasClass('dropdown-submenu');
24045 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24048 initEvents : function()
24050 if(this.isSubMenu){
24054 this.hidden = true;
24056 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24057 this.triggerEl.on('click', this.onTriggerPress, this);
24059 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24060 this.buttonEl.on('click', this.onClick, this);
24066 if(this.isSubMenu){
24070 return this.el.select('ul.dropdown-menu', true).first();
24073 onClick : function(e)
24075 this.fireEvent("click", this, e);
24078 onTriggerPress : function(e)
24080 if (this.isVisible()) {
24087 isVisible : function(){
24088 return !this.hidden;
24093 this.fireEvent("beforeshow", this);
24095 this.hidden = false;
24096 this.el.addClass('open');
24098 Roo.get(document).on("mouseup", this.onMouseUp, this);
24100 this.fireEvent("show", this);
24107 this.fireEvent("beforehide", this);
24109 this.hidden = true;
24110 this.el.removeClass('open');
24112 Roo.get(document).un("mouseup", this.onMouseUp);
24114 this.fireEvent("hide", this);
24117 onMouseUp : function()
24131 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24134 * @class Roo.bootstrap.menu.Item
24135 * @extends Roo.bootstrap.Component
24136 * Bootstrap MenuItem class
24137 * @cfg {Boolean} submenu (true | false) default false
24138 * @cfg {String} html text of the item
24139 * @cfg {String} href the link
24140 * @cfg {Boolean} disable (true | false) default false
24141 * @cfg {Boolean} preventDefault (true | false) default true
24142 * @cfg {String} icon Font awesome icon
24143 * @cfg {String} pos Submenu align to (left | right) default right
24147 * Create a new Item
24148 * @param {Object} config The config object
24152 Roo.bootstrap.menu.Item = function(config){
24153 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24157 * Fires when the mouse is hovering over this menu
24158 * @param {Roo.bootstrap.menu.Item} this
24159 * @param {Roo.EventObject} e
24164 * Fires when the mouse exits this menu
24165 * @param {Roo.bootstrap.menu.Item} this
24166 * @param {Roo.EventObject} e
24172 * The raw click event for the entire grid.
24173 * @param {Roo.EventObject} e
24179 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24184 preventDefault: true,
24189 getAutoCreate : function()
24194 cls : 'roo-menu-item-text',
24202 cls : 'fa ' + this.icon
24211 href : this.href || '#',
24218 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24222 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24224 if(this.pos == 'left'){
24225 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24232 initEvents : function()
24234 this.el.on('mouseover', this.onMouseOver, this);
24235 this.el.on('mouseout', this.onMouseOut, this);
24237 this.el.select('a', true).first().on('click', this.onClick, this);
24241 onClick : function(e)
24243 if(this.preventDefault){
24244 e.preventDefault();
24247 this.fireEvent("click", this, e);
24250 onMouseOver : function(e)
24252 if(this.submenu && this.pos == 'left'){
24253 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24256 this.fireEvent("mouseover", this, e);
24259 onMouseOut : function(e)
24261 this.fireEvent("mouseout", this, e);
24273 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24276 * @class Roo.bootstrap.menu.Separator
24277 * @extends Roo.bootstrap.Component
24278 * Bootstrap Separator class
24281 * Create a new Separator
24282 * @param {Object} config The config object
24286 Roo.bootstrap.menu.Separator = function(config){
24287 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24290 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24292 getAutoCreate : function(){
24313 * @class Roo.bootstrap.Tooltip
24314 * Bootstrap Tooltip class
24315 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24316 * to determine which dom element triggers the tooltip.
24318 * It needs to add support for additional attributes like tooltip-position
24321 * Create a new Toolti
24322 * @param {Object} config The config object
24325 Roo.bootstrap.Tooltip = function(config){
24326 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24329 Roo.apply(Roo.bootstrap.Tooltip, {
24331 * @function init initialize tooltip monitoring.
24335 currentTip : false,
24336 currentRegion : false,
24342 Roo.get(document).on('mouseover', this.enter ,this);
24343 Roo.get(document).on('mouseout', this.leave, this);
24346 this.currentTip = new Roo.bootstrap.Tooltip();
24349 enter : function(ev)
24351 var dom = ev.getTarget();
24353 //Roo.log(['enter',dom]);
24354 var el = Roo.fly(dom);
24355 if (this.currentEl) {
24357 //Roo.log(this.currentEl);
24358 //Roo.log(this.currentEl.contains(dom));
24359 if (this.currentEl == el) {
24362 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24368 if (this.currentTip.el) {
24369 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24373 if(!el || el.dom == document){
24379 // you can not look for children, as if el is the body.. then everythign is the child..
24380 if (!el.attr('tooltip')) { //
24381 if (!el.select("[tooltip]").elements.length) {
24384 // is the mouse over this child...?
24385 bindEl = el.select("[tooltip]").first();
24386 var xy = ev.getXY();
24387 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24388 //Roo.log("not in region.");
24391 //Roo.log("child element over..");
24394 this.currentEl = bindEl;
24395 this.currentTip.bind(bindEl);
24396 this.currentRegion = Roo.lib.Region.getRegion(dom);
24397 this.currentTip.enter();
24400 leave : function(ev)
24402 var dom = ev.getTarget();
24403 //Roo.log(['leave',dom]);
24404 if (!this.currentEl) {
24409 if (dom != this.currentEl.dom) {
24412 var xy = ev.getXY();
24413 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24416 // only activate leave if mouse cursor is outside... bounding box..
24421 if (this.currentTip) {
24422 this.currentTip.leave();
24424 //Roo.log('clear currentEl');
24425 this.currentEl = false;
24430 'left' : ['r-l', [-2,0], 'right'],
24431 'right' : ['l-r', [2,0], 'left'],
24432 'bottom' : ['t-b', [0,2], 'top'],
24433 'top' : [ 'b-t', [0,-2], 'bottom']
24439 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24444 delay : null, // can be { show : 300 , hide: 500}
24448 hoverState : null, //???
24450 placement : 'bottom',
24452 getAutoCreate : function(){
24459 cls : 'tooltip-arrow'
24462 cls : 'tooltip-inner'
24469 bind : function(el)
24475 enter : function () {
24477 if (this.timeout != null) {
24478 clearTimeout(this.timeout);
24481 this.hoverState = 'in';
24482 //Roo.log("enter - show");
24483 if (!this.delay || !this.delay.show) {
24488 this.timeout = setTimeout(function () {
24489 if (_t.hoverState == 'in') {
24492 }, this.delay.show);
24496 clearTimeout(this.timeout);
24498 this.hoverState = 'out';
24499 if (!this.delay || !this.delay.hide) {
24505 this.timeout = setTimeout(function () {
24506 //Roo.log("leave - timeout");
24508 if (_t.hoverState == 'out') {
24510 Roo.bootstrap.Tooltip.currentEl = false;
24518 this.render(document.body);
24521 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24523 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24525 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24527 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24529 var placement = typeof this.placement == 'function' ?
24530 this.placement.call(this, this.el, on_el) :
24533 var autoToken = /\s?auto?\s?/i;
24534 var autoPlace = autoToken.test(placement);
24536 placement = placement.replace(autoToken, '') || 'top';
24540 //this.el.setXY([0,0]);
24542 //this.el.dom.style.display='block';
24544 //this.el.appendTo(on_el);
24546 var p = this.getPosition();
24547 var box = this.el.getBox();
24553 var align = Roo.bootstrap.Tooltip.alignment[placement];
24555 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24557 if(placement == 'top' || placement == 'bottom'){
24559 placement = 'right';
24562 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24563 placement = 'left';
24566 var scroll = Roo.select('body', true).first().getScroll();
24568 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24574 align = Roo.bootstrap.Tooltip.alignment[placement];
24576 this.el.alignTo(this.bindEl, align[0],align[1]);
24577 //var arrow = this.el.select('.arrow',true).first();
24578 //arrow.set(align[2],
24580 this.el.addClass(placement);
24582 this.el.addClass('in fade');
24584 this.hoverState = null;
24586 if (this.el.hasClass('fade')) {
24597 //this.el.setXY([0,0]);
24598 this.el.removeClass('in');
24614 * @class Roo.bootstrap.LocationPicker
24615 * @extends Roo.bootstrap.Component
24616 * Bootstrap LocationPicker class
24617 * @cfg {Number} latitude Position when init default 0
24618 * @cfg {Number} longitude Position when init default 0
24619 * @cfg {Number} zoom default 15
24620 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24621 * @cfg {Boolean} mapTypeControl default false
24622 * @cfg {Boolean} disableDoubleClickZoom default false
24623 * @cfg {Boolean} scrollwheel default true
24624 * @cfg {Boolean} streetViewControl default false
24625 * @cfg {Number} radius default 0
24626 * @cfg {String} locationName
24627 * @cfg {Boolean} draggable default true
24628 * @cfg {Boolean} enableAutocomplete default false
24629 * @cfg {Boolean} enableReverseGeocode default true
24630 * @cfg {String} markerTitle
24633 * Create a new LocationPicker
24634 * @param {Object} config The config object
24638 Roo.bootstrap.LocationPicker = function(config){
24640 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24645 * Fires when the picker initialized.
24646 * @param {Roo.bootstrap.LocationPicker} this
24647 * @param {Google Location} location
24651 * @event positionchanged
24652 * Fires when the picker position changed.
24653 * @param {Roo.bootstrap.LocationPicker} this
24654 * @param {Google Location} location
24656 positionchanged : true,
24659 * Fires when the map resize.
24660 * @param {Roo.bootstrap.LocationPicker} this
24665 * Fires when the map show.
24666 * @param {Roo.bootstrap.LocationPicker} this
24671 * Fires when the map hide.
24672 * @param {Roo.bootstrap.LocationPicker} this
24677 * Fires when click the map.
24678 * @param {Roo.bootstrap.LocationPicker} this
24679 * @param {Map event} e
24683 * @event mapRightClick
24684 * Fires when right click the map.
24685 * @param {Roo.bootstrap.LocationPicker} this
24686 * @param {Map event} e
24688 mapRightClick : true,
24690 * @event markerClick
24691 * Fires when click the marker.
24692 * @param {Roo.bootstrap.LocationPicker} this
24693 * @param {Map event} e
24695 markerClick : true,
24697 * @event markerRightClick
24698 * Fires when right click the marker.
24699 * @param {Roo.bootstrap.LocationPicker} this
24700 * @param {Map event} e
24702 markerRightClick : true,
24704 * @event OverlayViewDraw
24705 * Fires when OverlayView Draw
24706 * @param {Roo.bootstrap.LocationPicker} this
24708 OverlayViewDraw : true,
24710 * @event OverlayViewOnAdd
24711 * Fires when OverlayView Draw
24712 * @param {Roo.bootstrap.LocationPicker} this
24714 OverlayViewOnAdd : true,
24716 * @event OverlayViewOnRemove
24717 * Fires when OverlayView Draw
24718 * @param {Roo.bootstrap.LocationPicker} this
24720 OverlayViewOnRemove : true,
24722 * @event OverlayViewShow
24723 * Fires when OverlayView Draw
24724 * @param {Roo.bootstrap.LocationPicker} this
24725 * @param {Pixel} cpx
24727 OverlayViewShow : true,
24729 * @event OverlayViewHide
24730 * Fires when OverlayView Draw
24731 * @param {Roo.bootstrap.LocationPicker} this
24733 OverlayViewHide : true,
24735 * @event loadexception
24736 * Fires when load google lib failed.
24737 * @param {Roo.bootstrap.LocationPicker} this
24739 loadexception : true
24744 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24746 gMapContext: false,
24752 mapTypeControl: false,
24753 disableDoubleClickZoom: false,
24755 streetViewControl: false,
24759 enableAutocomplete: false,
24760 enableReverseGeocode: true,
24763 getAutoCreate: function()
24768 cls: 'roo-location-picker'
24774 initEvents: function(ct, position)
24776 if(!this.el.getWidth() || this.isApplied()){
24780 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24785 initial: function()
24787 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24788 this.fireEvent('loadexception', this);
24792 if(!this.mapTypeId){
24793 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24796 this.gMapContext = this.GMapContext();
24798 this.initOverlayView();
24800 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24804 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24805 _this.setPosition(_this.gMapContext.marker.position);
24808 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24809 _this.fireEvent('mapClick', this, event);
24813 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24814 _this.fireEvent('mapRightClick', this, event);
24818 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24819 _this.fireEvent('markerClick', this, event);
24823 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24824 _this.fireEvent('markerRightClick', this, event);
24828 this.setPosition(this.gMapContext.location);
24830 this.fireEvent('initial', this, this.gMapContext.location);
24833 initOverlayView: function()
24837 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24841 _this.fireEvent('OverlayViewDraw', _this);
24846 _this.fireEvent('OverlayViewOnAdd', _this);
24849 onRemove: function()
24851 _this.fireEvent('OverlayViewOnRemove', _this);
24854 show: function(cpx)
24856 _this.fireEvent('OverlayViewShow', _this, cpx);
24861 _this.fireEvent('OverlayViewHide', _this);
24867 fromLatLngToContainerPixel: function(event)
24869 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24872 isApplied: function()
24874 return this.getGmapContext() == false ? false : true;
24877 getGmapContext: function()
24879 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24882 GMapContext: function()
24884 var position = new google.maps.LatLng(this.latitude, this.longitude);
24886 var _map = new google.maps.Map(this.el.dom, {
24889 mapTypeId: this.mapTypeId,
24890 mapTypeControl: this.mapTypeControl,
24891 disableDoubleClickZoom: this.disableDoubleClickZoom,
24892 scrollwheel: this.scrollwheel,
24893 streetViewControl: this.streetViewControl,
24894 locationName: this.locationName,
24895 draggable: this.draggable,
24896 enableAutocomplete: this.enableAutocomplete,
24897 enableReverseGeocode: this.enableReverseGeocode
24900 var _marker = new google.maps.Marker({
24901 position: position,
24903 title: this.markerTitle,
24904 draggable: this.draggable
24911 location: position,
24912 radius: this.radius,
24913 locationName: this.locationName,
24914 addressComponents: {
24915 formatted_address: null,
24916 addressLine1: null,
24917 addressLine2: null,
24919 streetNumber: null,
24923 stateOrProvince: null
24926 domContainer: this.el.dom,
24927 geodecoder: new google.maps.Geocoder()
24931 drawCircle: function(center, radius, options)
24933 if (this.gMapContext.circle != null) {
24934 this.gMapContext.circle.setMap(null);
24938 options = Roo.apply({}, options, {
24939 strokeColor: "#0000FF",
24940 strokeOpacity: .35,
24942 fillColor: "#0000FF",
24946 options.map = this.gMapContext.map;
24947 options.radius = radius;
24948 options.center = center;
24949 this.gMapContext.circle = new google.maps.Circle(options);
24950 return this.gMapContext.circle;
24956 setPosition: function(location)
24958 this.gMapContext.location = location;
24959 this.gMapContext.marker.setPosition(location);
24960 this.gMapContext.map.panTo(location);
24961 this.drawCircle(location, this.gMapContext.radius, {});
24965 if (this.gMapContext.settings.enableReverseGeocode) {
24966 this.gMapContext.geodecoder.geocode({
24967 latLng: this.gMapContext.location
24968 }, function(results, status) {
24970 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24971 _this.gMapContext.locationName = results[0].formatted_address;
24972 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24974 _this.fireEvent('positionchanged', this, location);
24981 this.fireEvent('positionchanged', this, location);
24986 google.maps.event.trigger(this.gMapContext.map, "resize");
24988 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24990 this.fireEvent('resize', this);
24993 setPositionByLatLng: function(latitude, longitude)
24995 this.setPosition(new google.maps.LatLng(latitude, longitude));
24998 getCurrentPosition: function()
25001 latitude: this.gMapContext.location.lat(),
25002 longitude: this.gMapContext.location.lng()
25006 getAddressName: function()
25008 return this.gMapContext.locationName;
25011 getAddressComponents: function()
25013 return this.gMapContext.addressComponents;
25016 address_component_from_google_geocode: function(address_components)
25020 for (var i = 0; i < address_components.length; i++) {
25021 var component = address_components[i];
25022 if (component.types.indexOf("postal_code") >= 0) {
25023 result.postalCode = component.short_name;
25024 } else if (component.types.indexOf("street_number") >= 0) {
25025 result.streetNumber = component.short_name;
25026 } else if (component.types.indexOf("route") >= 0) {
25027 result.streetName = component.short_name;
25028 } else if (component.types.indexOf("neighborhood") >= 0) {
25029 result.city = component.short_name;
25030 } else if (component.types.indexOf("locality") >= 0) {
25031 result.city = component.short_name;
25032 } else if (component.types.indexOf("sublocality") >= 0) {
25033 result.district = component.short_name;
25034 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25035 result.stateOrProvince = component.short_name;
25036 } else if (component.types.indexOf("country") >= 0) {
25037 result.country = component.short_name;
25041 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25042 result.addressLine2 = "";
25046 setZoomLevel: function(zoom)
25048 this.gMapContext.map.setZoom(zoom);
25061 this.fireEvent('show', this);
25072 this.fireEvent('hide', this);
25077 Roo.apply(Roo.bootstrap.LocationPicker, {
25079 OverlayView : function(map, options)
25081 options = options || {};
25095 * @class Roo.bootstrap.Alert
25096 * @extends Roo.bootstrap.Component
25097 * Bootstrap Alert class
25098 * @cfg {String} title The title of alert
25099 * @cfg {String} html The content of alert
25100 * @cfg {String} weight ( success | info | warning | danger )
25101 * @cfg {String} faicon font-awesomeicon
25104 * Create a new alert
25105 * @param {Object} config The config object
25109 Roo.bootstrap.Alert = function(config){
25110 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25114 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25121 getAutoCreate : function()
25130 cls : 'roo-alert-icon'
25135 cls : 'roo-alert-title',
25140 cls : 'roo-alert-text',
25147 cfg.cn[0].cls += ' fa ' + this.faicon;
25151 cfg.cls += ' alert-' + this.weight;
25157 initEvents: function()
25159 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25162 setTitle : function(str)
25164 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25167 setText : function(str)
25169 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25172 setWeight : function(weight)
25175 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25178 this.weight = weight;
25180 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25183 setIcon : function(icon)
25186 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25189 this.faicon = icon;
25191 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25212 * @class Roo.bootstrap.UploadCropbox
25213 * @extends Roo.bootstrap.Component
25214 * Bootstrap UploadCropbox class
25215 * @cfg {String} emptyText show when image has been loaded
25216 * @cfg {String} rotateNotify show when image too small to rotate
25217 * @cfg {Number} errorTimeout default 3000
25218 * @cfg {Number} minWidth default 300
25219 * @cfg {Number} minHeight default 300
25220 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25221 * @cfg {Boolean} isDocument (true|false) default false
25222 * @cfg {String} url action url
25223 * @cfg {String} paramName default 'imageUpload'
25224 * @cfg {String} method default POST
25225 * @cfg {Boolean} loadMask (true|false) default true
25226 * @cfg {Boolean} loadingText default 'Loading...'
25229 * Create a new UploadCropbox
25230 * @param {Object} config The config object
25233 Roo.bootstrap.UploadCropbox = function(config){
25234 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25238 * @event beforeselectfile
25239 * Fire before select file
25240 * @param {Roo.bootstrap.UploadCropbox} this
25242 "beforeselectfile" : true,
25245 * Fire after initEvent
25246 * @param {Roo.bootstrap.UploadCropbox} this
25251 * Fire after initEvent
25252 * @param {Roo.bootstrap.UploadCropbox} this
25253 * @param {String} data
25258 * Fire when preparing the file data
25259 * @param {Roo.bootstrap.UploadCropbox} this
25260 * @param {Object} file
25265 * Fire when get exception
25266 * @param {Roo.bootstrap.UploadCropbox} this
25267 * @param {XMLHttpRequest} xhr
25269 "exception" : true,
25271 * @event beforeloadcanvas
25272 * Fire before load the canvas
25273 * @param {Roo.bootstrap.UploadCropbox} this
25274 * @param {String} src
25276 "beforeloadcanvas" : true,
25279 * Fire when trash image
25280 * @param {Roo.bootstrap.UploadCropbox} this
25285 * Fire when download the image
25286 * @param {Roo.bootstrap.UploadCropbox} this
25290 * @event footerbuttonclick
25291 * Fire when footerbuttonclick
25292 * @param {Roo.bootstrap.UploadCropbox} this
25293 * @param {String} type
25295 "footerbuttonclick" : true,
25299 * @param {Roo.bootstrap.UploadCropbox} this
25304 * Fire when rotate the image
25305 * @param {Roo.bootstrap.UploadCropbox} this
25306 * @param {String} pos
25311 * Fire when inspect the file
25312 * @param {Roo.bootstrap.UploadCropbox} this
25313 * @param {Object} file
25318 * Fire when xhr upload the file
25319 * @param {Roo.bootstrap.UploadCropbox} this
25320 * @param {Object} data
25325 * Fire when arrange the file data
25326 * @param {Roo.bootstrap.UploadCropbox} this
25327 * @param {Object} formData
25332 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25335 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25337 emptyText : 'Click to upload image',
25338 rotateNotify : 'Image is too small to rotate',
25339 errorTimeout : 3000,
25353 cropType : 'image/jpeg',
25355 canvasLoaded : false,
25356 isDocument : false,
25358 paramName : 'imageUpload',
25360 loadingText : 'Loading...',
25363 getAutoCreate : function()
25367 cls : 'roo-upload-cropbox',
25371 cls : 'roo-upload-cropbox-selector',
25376 cls : 'roo-upload-cropbox-body',
25377 style : 'cursor:pointer',
25381 cls : 'roo-upload-cropbox-preview'
25385 cls : 'roo-upload-cropbox-thumb'
25389 cls : 'roo-upload-cropbox-empty-notify',
25390 html : this.emptyText
25394 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25395 html : this.rotateNotify
25401 cls : 'roo-upload-cropbox-footer',
25404 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25414 onRender : function(ct, position)
25416 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25418 if (this.buttons.length) {
25420 Roo.each(this.buttons, function(bb) {
25422 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25424 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25430 this.maskEl = this.el;
25434 initEvents : function()
25436 this.urlAPI = (window.createObjectURL && window) ||
25437 (window.URL && URL.revokeObjectURL && URL) ||
25438 (window.webkitURL && webkitURL);
25440 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25441 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25443 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25444 this.selectorEl.hide();
25446 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25447 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25449 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25450 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25451 this.thumbEl.hide();
25453 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25454 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25456 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25457 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25458 this.errorEl.hide();
25460 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25461 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25462 this.footerEl.hide();
25464 this.setThumbBoxSize();
25470 this.fireEvent('initial', this);
25477 window.addEventListener("resize", function() { _this.resize(); } );
25479 this.bodyEl.on('click', this.beforeSelectFile, this);
25482 this.bodyEl.on('touchstart', this.onTouchStart, this);
25483 this.bodyEl.on('touchmove', this.onTouchMove, this);
25484 this.bodyEl.on('touchend', this.onTouchEnd, this);
25488 this.bodyEl.on('mousedown', this.onMouseDown, this);
25489 this.bodyEl.on('mousemove', this.onMouseMove, this);
25490 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25491 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25492 Roo.get(document).on('mouseup', this.onMouseUp, this);
25495 this.selectorEl.on('change', this.onFileSelected, this);
25501 this.baseScale = 1;
25503 this.baseRotate = 1;
25504 this.dragable = false;
25505 this.pinching = false;
25508 this.cropData = false;
25509 this.notifyEl.dom.innerHTML = this.emptyText;
25511 this.selectorEl.dom.value = '';
25515 resize : function()
25517 if(this.fireEvent('resize', this) != false){
25518 this.setThumbBoxPosition();
25519 this.setCanvasPosition();
25523 onFooterButtonClick : function(e, el, o, type)
25526 case 'rotate-left' :
25527 this.onRotateLeft(e);
25529 case 'rotate-right' :
25530 this.onRotateRight(e);
25533 this.beforeSelectFile(e);
25548 this.fireEvent('footerbuttonclick', this, type);
25551 beforeSelectFile : function(e)
25553 e.preventDefault();
25555 if(this.fireEvent('beforeselectfile', this) != false){
25556 this.selectorEl.dom.click();
25560 onFileSelected : function(e)
25562 e.preventDefault();
25564 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25568 var file = this.selectorEl.dom.files[0];
25570 if(this.fireEvent('inspect', this, file) != false){
25571 this.prepare(file);
25576 trash : function(e)
25578 this.fireEvent('trash', this);
25581 download : function(e)
25583 this.fireEvent('download', this);
25586 loadCanvas : function(src)
25588 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25592 this.imageEl = document.createElement('img');
25596 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25598 this.imageEl.src = src;
25602 onLoadCanvas : function()
25604 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25605 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25607 this.bodyEl.un('click', this.beforeSelectFile, this);
25609 this.notifyEl.hide();
25610 this.thumbEl.show();
25611 this.footerEl.show();
25613 this.baseRotateLevel();
25615 if(this.isDocument){
25616 this.setThumbBoxSize();
25619 this.setThumbBoxPosition();
25621 this.baseScaleLevel();
25627 this.canvasLoaded = true;
25630 this.maskEl.unmask();
25635 setCanvasPosition : function()
25637 if(!this.canvasEl){
25641 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25642 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25644 this.previewEl.setLeft(pw);
25645 this.previewEl.setTop(ph);
25649 onMouseDown : function(e)
25653 this.dragable = true;
25654 this.pinching = false;
25656 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25657 this.dragable = false;
25661 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25662 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25666 onMouseMove : function(e)
25670 if(!this.canvasLoaded){
25674 if (!this.dragable){
25678 var minX = Math.ceil(this.thumbEl.getLeft(true));
25679 var minY = Math.ceil(this.thumbEl.getTop(true));
25681 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25682 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25684 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25685 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25687 x = x - this.mouseX;
25688 y = y - this.mouseY;
25690 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25691 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25693 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25694 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25696 this.previewEl.setLeft(bgX);
25697 this.previewEl.setTop(bgY);
25699 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25700 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25703 onMouseUp : function(e)
25707 this.dragable = false;
25710 onMouseWheel : function(e)
25714 this.startScale = this.scale;
25716 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25718 if(!this.zoomable()){
25719 this.scale = this.startScale;
25728 zoomable : function()
25730 var minScale = this.thumbEl.getWidth() / this.minWidth;
25732 if(this.minWidth < this.minHeight){
25733 minScale = this.thumbEl.getHeight() / this.minHeight;
25736 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25737 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25741 (this.rotate == 0 || this.rotate == 180) &&
25743 width > this.imageEl.OriginWidth ||
25744 height > this.imageEl.OriginHeight ||
25745 (width < this.minWidth && height < this.minHeight)
25753 (this.rotate == 90 || this.rotate == 270) &&
25755 width > this.imageEl.OriginWidth ||
25756 height > this.imageEl.OriginHeight ||
25757 (width < this.minHeight && height < this.minWidth)
25764 !this.isDocument &&
25765 (this.rotate == 0 || this.rotate == 180) &&
25767 width < this.minWidth ||
25768 width > this.imageEl.OriginWidth ||
25769 height < this.minHeight ||
25770 height > this.imageEl.OriginHeight
25777 !this.isDocument &&
25778 (this.rotate == 90 || this.rotate == 270) &&
25780 width < this.minHeight ||
25781 width > this.imageEl.OriginWidth ||
25782 height < this.minWidth ||
25783 height > this.imageEl.OriginHeight
25793 onRotateLeft : function(e)
25795 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25797 var minScale = this.thumbEl.getWidth() / this.minWidth;
25799 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25800 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25802 this.startScale = this.scale;
25804 while (this.getScaleLevel() < minScale){
25806 this.scale = this.scale + 1;
25808 if(!this.zoomable()){
25813 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25814 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25819 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25826 this.scale = this.startScale;
25828 this.onRotateFail();
25833 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25835 if(this.isDocument){
25836 this.setThumbBoxSize();
25837 this.setThumbBoxPosition();
25838 this.setCanvasPosition();
25843 this.fireEvent('rotate', this, 'left');
25847 onRotateRight : function(e)
25849 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25851 var minScale = this.thumbEl.getWidth() / this.minWidth;
25853 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25854 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25856 this.startScale = this.scale;
25858 while (this.getScaleLevel() < minScale){
25860 this.scale = this.scale + 1;
25862 if(!this.zoomable()){
25867 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25868 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25873 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25880 this.scale = this.startScale;
25882 this.onRotateFail();
25887 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25889 if(this.isDocument){
25890 this.setThumbBoxSize();
25891 this.setThumbBoxPosition();
25892 this.setCanvasPosition();
25897 this.fireEvent('rotate', this, 'right');
25900 onRotateFail : function()
25902 this.errorEl.show(true);
25906 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25911 this.previewEl.dom.innerHTML = '';
25913 var canvasEl = document.createElement("canvas");
25915 var contextEl = canvasEl.getContext("2d");
25917 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25918 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25919 var center = this.imageEl.OriginWidth / 2;
25921 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25922 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25923 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25924 center = this.imageEl.OriginHeight / 2;
25927 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25929 contextEl.translate(center, center);
25930 contextEl.rotate(this.rotate * Math.PI / 180);
25932 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25934 this.canvasEl = document.createElement("canvas");
25936 this.contextEl = this.canvasEl.getContext("2d");
25938 switch (this.rotate) {
25941 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25942 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25944 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25949 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25950 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25952 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25953 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);
25957 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25962 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25963 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25965 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25966 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);
25970 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);
25975 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25976 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25978 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25979 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25983 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);
25990 this.previewEl.appendChild(this.canvasEl);
25992 this.setCanvasPosition();
25997 if(!this.canvasLoaded){
26001 var imageCanvas = document.createElement("canvas");
26003 var imageContext = imageCanvas.getContext("2d");
26005 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26006 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26008 var center = imageCanvas.width / 2;
26010 imageContext.translate(center, center);
26012 imageContext.rotate(this.rotate * Math.PI / 180);
26014 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26016 var canvas = document.createElement("canvas");
26018 var context = canvas.getContext("2d");
26020 canvas.width = this.minWidth;
26021 canvas.height = this.minHeight;
26023 switch (this.rotate) {
26026 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26027 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26029 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26030 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26032 var targetWidth = this.minWidth - 2 * x;
26033 var targetHeight = this.minHeight - 2 * y;
26037 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26038 scale = targetWidth / width;
26041 if(x > 0 && y == 0){
26042 scale = targetHeight / height;
26045 if(x > 0 && y > 0){
26046 scale = targetWidth / width;
26048 if(width < height){
26049 scale = targetHeight / height;
26053 context.scale(scale, scale);
26055 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26056 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26058 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26059 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26061 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26066 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26067 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26069 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26070 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26072 var targetWidth = this.minWidth - 2 * x;
26073 var targetHeight = this.minHeight - 2 * y;
26077 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26078 scale = targetWidth / width;
26081 if(x > 0 && y == 0){
26082 scale = targetHeight / height;
26085 if(x > 0 && y > 0){
26086 scale = targetWidth / width;
26088 if(width < height){
26089 scale = targetHeight / height;
26093 context.scale(scale, scale);
26095 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26096 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26098 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26099 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26101 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26103 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26108 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26109 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26111 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26112 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26114 var targetWidth = this.minWidth - 2 * x;
26115 var targetHeight = this.minHeight - 2 * y;
26119 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26120 scale = targetWidth / width;
26123 if(x > 0 && y == 0){
26124 scale = targetHeight / height;
26127 if(x > 0 && y > 0){
26128 scale = targetWidth / width;
26130 if(width < height){
26131 scale = targetHeight / height;
26135 context.scale(scale, scale);
26137 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26138 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26140 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26141 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26143 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26144 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26146 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26151 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26152 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26154 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26155 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26157 var targetWidth = this.minWidth - 2 * x;
26158 var targetHeight = this.minHeight - 2 * y;
26162 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26163 scale = targetWidth / width;
26166 if(x > 0 && y == 0){
26167 scale = targetHeight / height;
26170 if(x > 0 && y > 0){
26171 scale = targetWidth / width;
26173 if(width < height){
26174 scale = targetHeight / height;
26178 context.scale(scale, scale);
26180 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26181 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26183 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26184 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26186 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26188 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26195 this.cropData = canvas.toDataURL(this.cropType);
26197 if(this.fireEvent('crop', this, this.cropData) !== false){
26198 this.process(this.file, this.cropData);
26205 setThumbBoxSize : function()
26209 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26210 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26211 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26213 this.minWidth = width;
26214 this.minHeight = height;
26216 if(this.rotate == 90 || this.rotate == 270){
26217 this.minWidth = height;
26218 this.minHeight = width;
26223 width = Math.ceil(this.minWidth * height / this.minHeight);
26225 if(this.minWidth > this.minHeight){
26227 height = Math.ceil(this.minHeight * width / this.minWidth);
26230 this.thumbEl.setStyle({
26231 width : width + 'px',
26232 height : height + 'px'
26239 setThumbBoxPosition : function()
26241 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26242 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26244 this.thumbEl.setLeft(x);
26245 this.thumbEl.setTop(y);
26249 baseRotateLevel : function()
26251 this.baseRotate = 1;
26254 typeof(this.exif) != 'undefined' &&
26255 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26256 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26258 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26261 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26265 baseScaleLevel : function()
26269 if(this.isDocument){
26271 if(this.baseRotate == 6 || this.baseRotate == 8){
26273 height = this.thumbEl.getHeight();
26274 this.baseScale = height / this.imageEl.OriginWidth;
26276 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26277 width = this.thumbEl.getWidth();
26278 this.baseScale = width / this.imageEl.OriginHeight;
26284 height = this.thumbEl.getHeight();
26285 this.baseScale = height / this.imageEl.OriginHeight;
26287 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26288 width = this.thumbEl.getWidth();
26289 this.baseScale = width / this.imageEl.OriginWidth;
26295 if(this.baseRotate == 6 || this.baseRotate == 8){
26297 width = this.thumbEl.getHeight();
26298 this.baseScale = width / this.imageEl.OriginHeight;
26300 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26301 height = this.thumbEl.getWidth();
26302 this.baseScale = height / this.imageEl.OriginHeight;
26305 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26306 height = this.thumbEl.getWidth();
26307 this.baseScale = height / this.imageEl.OriginHeight;
26309 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26310 width = this.thumbEl.getHeight();
26311 this.baseScale = width / this.imageEl.OriginWidth;
26318 width = this.thumbEl.getWidth();
26319 this.baseScale = width / this.imageEl.OriginWidth;
26321 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26322 height = this.thumbEl.getHeight();
26323 this.baseScale = height / this.imageEl.OriginHeight;
26326 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26328 height = this.thumbEl.getHeight();
26329 this.baseScale = height / this.imageEl.OriginHeight;
26331 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26332 width = this.thumbEl.getWidth();
26333 this.baseScale = width / this.imageEl.OriginWidth;
26341 getScaleLevel : function()
26343 return this.baseScale * Math.pow(1.1, this.scale);
26346 onTouchStart : function(e)
26348 if(!this.canvasLoaded){
26349 this.beforeSelectFile(e);
26353 var touches = e.browserEvent.touches;
26359 if(touches.length == 1){
26360 this.onMouseDown(e);
26364 if(touches.length != 2){
26370 for(var i = 0, finger; finger = touches[i]; i++){
26371 coords.push(finger.pageX, finger.pageY);
26374 var x = Math.pow(coords[0] - coords[2], 2);
26375 var y = Math.pow(coords[1] - coords[3], 2);
26377 this.startDistance = Math.sqrt(x + y);
26379 this.startScale = this.scale;
26381 this.pinching = true;
26382 this.dragable = false;
26386 onTouchMove : function(e)
26388 if(!this.pinching && !this.dragable){
26392 var touches = e.browserEvent.touches;
26399 this.onMouseMove(e);
26405 for(var i = 0, finger; finger = touches[i]; i++){
26406 coords.push(finger.pageX, finger.pageY);
26409 var x = Math.pow(coords[0] - coords[2], 2);
26410 var y = Math.pow(coords[1] - coords[3], 2);
26412 this.endDistance = Math.sqrt(x + y);
26414 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26416 if(!this.zoomable()){
26417 this.scale = this.startScale;
26425 onTouchEnd : function(e)
26427 this.pinching = false;
26428 this.dragable = false;
26432 process : function(file, crop)
26435 this.maskEl.mask(this.loadingText);
26438 this.xhr = new XMLHttpRequest();
26440 file.xhr = this.xhr;
26442 this.xhr.open(this.method, this.url, true);
26445 "Accept": "application/json",
26446 "Cache-Control": "no-cache",
26447 "X-Requested-With": "XMLHttpRequest"
26450 for (var headerName in headers) {
26451 var headerValue = headers[headerName];
26453 this.xhr.setRequestHeader(headerName, headerValue);
26459 this.xhr.onload = function()
26461 _this.xhrOnLoad(_this.xhr);
26464 this.xhr.onerror = function()
26466 _this.xhrOnError(_this.xhr);
26469 var formData = new FormData();
26471 formData.append('returnHTML', 'NO');
26474 formData.append('crop', crop);
26477 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26478 formData.append(this.paramName, file, file.name);
26481 if(typeof(file.filename) != 'undefined'){
26482 formData.append('filename', file.filename);
26485 if(typeof(file.mimetype) != 'undefined'){
26486 formData.append('mimetype', file.mimetype);
26489 if(this.fireEvent('arrange', this, formData) != false){
26490 this.xhr.send(formData);
26494 xhrOnLoad : function(xhr)
26497 this.maskEl.unmask();
26500 if (xhr.readyState !== 4) {
26501 this.fireEvent('exception', this, xhr);
26505 var response = Roo.decode(xhr.responseText);
26507 if(!response.success){
26508 this.fireEvent('exception', this, xhr);
26512 var response = Roo.decode(xhr.responseText);
26514 this.fireEvent('upload', this, response);
26518 xhrOnError : function()
26521 this.maskEl.unmask();
26524 Roo.log('xhr on error');
26526 var response = Roo.decode(xhr.responseText);
26532 prepare : function(file)
26535 this.maskEl.mask(this.loadingText);
26541 if(typeof(file) === 'string'){
26542 this.loadCanvas(file);
26546 if(!file || !this.urlAPI){
26551 this.cropType = file.type;
26555 if(this.fireEvent('prepare', this, this.file) != false){
26557 var reader = new FileReader();
26559 reader.onload = function (e) {
26560 if (e.target.error) {
26561 Roo.log(e.target.error);
26565 var buffer = e.target.result,
26566 dataView = new DataView(buffer),
26568 maxOffset = dataView.byteLength - 4,
26572 if (dataView.getUint16(0) === 0xffd8) {
26573 while (offset < maxOffset) {
26574 markerBytes = dataView.getUint16(offset);
26576 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26577 markerLength = dataView.getUint16(offset + 2) + 2;
26578 if (offset + markerLength > dataView.byteLength) {
26579 Roo.log('Invalid meta data: Invalid segment size.');
26583 if(markerBytes == 0xffe1){
26584 _this.parseExifData(
26591 offset += markerLength;
26601 var url = _this.urlAPI.createObjectURL(_this.file);
26603 _this.loadCanvas(url);
26608 reader.readAsArrayBuffer(this.file);
26614 parseExifData : function(dataView, offset, length)
26616 var tiffOffset = offset + 10,
26620 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26621 // No Exif data, might be XMP data instead
26625 // Check for the ASCII code for "Exif" (0x45786966):
26626 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26627 // No Exif data, might be XMP data instead
26630 if (tiffOffset + 8 > dataView.byteLength) {
26631 Roo.log('Invalid Exif data: Invalid segment size.');
26634 // Check for the two null bytes:
26635 if (dataView.getUint16(offset + 8) !== 0x0000) {
26636 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26639 // Check the byte alignment:
26640 switch (dataView.getUint16(tiffOffset)) {
26642 littleEndian = true;
26645 littleEndian = false;
26648 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26651 // Check for the TIFF tag marker (0x002A):
26652 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26653 Roo.log('Invalid Exif data: Missing TIFF marker.');
26656 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26657 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26659 this.parseExifTags(
26662 tiffOffset + dirOffset,
26667 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26672 if (dirOffset + 6 > dataView.byteLength) {
26673 Roo.log('Invalid Exif data: Invalid directory offset.');
26676 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26677 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26678 if (dirEndOffset + 4 > dataView.byteLength) {
26679 Roo.log('Invalid Exif data: Invalid directory size.');
26682 for (i = 0; i < tagsNumber; i += 1) {
26686 dirOffset + 2 + 12 * i, // tag offset
26690 // Return the offset to the next directory:
26691 return dataView.getUint32(dirEndOffset, littleEndian);
26694 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26696 var tag = dataView.getUint16(offset, littleEndian);
26698 this.exif[tag] = this.getExifValue(
26702 dataView.getUint16(offset + 2, littleEndian), // tag type
26703 dataView.getUint32(offset + 4, littleEndian), // tag length
26708 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26710 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26719 Roo.log('Invalid Exif data: Invalid tag type.');
26723 tagSize = tagType.size * length;
26724 // Determine if the value is contained in the dataOffset bytes,
26725 // or if the value at the dataOffset is a pointer to the actual data:
26726 dataOffset = tagSize > 4 ?
26727 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26728 if (dataOffset + tagSize > dataView.byteLength) {
26729 Roo.log('Invalid Exif data: Invalid data offset.');
26732 if (length === 1) {
26733 return tagType.getValue(dataView, dataOffset, littleEndian);
26736 for (i = 0; i < length; i += 1) {
26737 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26740 if (tagType.ascii) {
26742 // Concatenate the chars:
26743 for (i = 0; i < values.length; i += 1) {
26745 // Ignore the terminating NULL byte(s):
26746 if (c === '\u0000') {
26758 Roo.apply(Roo.bootstrap.UploadCropbox, {
26760 'Orientation': 0x0112
26764 1: 0, //'top-left',
26766 3: 180, //'bottom-right',
26767 // 4: 'bottom-left',
26769 6: 90, //'right-top',
26770 // 7: 'right-bottom',
26771 8: 270 //'left-bottom'
26775 // byte, 8-bit unsigned int:
26777 getValue: function (dataView, dataOffset) {
26778 return dataView.getUint8(dataOffset);
26782 // ascii, 8-bit byte:
26784 getValue: function (dataView, dataOffset) {
26785 return String.fromCharCode(dataView.getUint8(dataOffset));
26790 // short, 16 bit int:
26792 getValue: function (dataView, dataOffset, littleEndian) {
26793 return dataView.getUint16(dataOffset, littleEndian);
26797 // long, 32 bit int:
26799 getValue: function (dataView, dataOffset, littleEndian) {
26800 return dataView.getUint32(dataOffset, littleEndian);
26804 // rational = two long values, first is numerator, second is denominator:
26806 getValue: function (dataView, dataOffset, littleEndian) {
26807 return dataView.getUint32(dataOffset, littleEndian) /
26808 dataView.getUint32(dataOffset + 4, littleEndian);
26812 // slong, 32 bit signed int:
26814 getValue: function (dataView, dataOffset, littleEndian) {
26815 return dataView.getInt32(dataOffset, littleEndian);
26819 // srational, two slongs, first is numerator, second is denominator:
26821 getValue: function (dataView, dataOffset, littleEndian) {
26822 return dataView.getInt32(dataOffset, littleEndian) /
26823 dataView.getInt32(dataOffset + 4, littleEndian);
26833 cls : 'btn-group roo-upload-cropbox-rotate-left',
26834 action : 'rotate-left',
26838 cls : 'btn btn-default',
26839 html : '<i class="fa fa-undo"></i>'
26845 cls : 'btn-group roo-upload-cropbox-picture',
26846 action : 'picture',
26850 cls : 'btn btn-default',
26851 html : '<i class="fa fa-picture-o"></i>'
26857 cls : 'btn-group roo-upload-cropbox-rotate-right',
26858 action : 'rotate-right',
26862 cls : 'btn btn-default',
26863 html : '<i class="fa fa-repeat"></i>'
26871 cls : 'btn-group roo-upload-cropbox-rotate-left',
26872 action : 'rotate-left',
26876 cls : 'btn btn-default',
26877 html : '<i class="fa fa-undo"></i>'
26883 cls : 'btn-group roo-upload-cropbox-download',
26884 action : 'download',
26888 cls : 'btn btn-default',
26889 html : '<i class="fa fa-download"></i>'
26895 cls : 'btn-group roo-upload-cropbox-crop',
26900 cls : 'btn btn-default',
26901 html : '<i class="fa fa-crop"></i>'
26907 cls : 'btn-group roo-upload-cropbox-trash',
26912 cls : 'btn btn-default',
26913 html : '<i class="fa fa-trash"></i>'
26919 cls : 'btn-group roo-upload-cropbox-rotate-right',
26920 action : 'rotate-right',
26924 cls : 'btn btn-default',
26925 html : '<i class="fa fa-repeat"></i>'
26933 cls : 'btn-group roo-upload-cropbox-rotate-left',
26934 action : 'rotate-left',
26938 cls : 'btn btn-default',
26939 html : '<i class="fa fa-undo"></i>'
26945 cls : 'btn-group roo-upload-cropbox-rotate-right',
26946 action : 'rotate-right',
26950 cls : 'btn btn-default',
26951 html : '<i class="fa fa-repeat"></i>'
26964 * @class Roo.bootstrap.DocumentManager
26965 * @extends Roo.bootstrap.Component
26966 * Bootstrap DocumentManager class
26967 * @cfg {String} paramName default 'imageUpload'
26968 * @cfg {String} method default POST
26969 * @cfg {String} url action url
26970 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26971 * @cfg {Boolean} multiple multiple upload default true
26972 * @cfg {Number} thumbSize default 300
26973 * @cfg {String} fieldLabel
26974 * @cfg {Number} labelWidth default 4
26975 * @cfg {String} labelAlign (left|top) default left
26976 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26979 * Create a new DocumentManager
26980 * @param {Object} config The config object
26983 Roo.bootstrap.DocumentManager = function(config){
26984 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26989 * Fire when initial the DocumentManager
26990 * @param {Roo.bootstrap.DocumentManager} this
26995 * inspect selected file
26996 * @param {Roo.bootstrap.DocumentManager} this
26997 * @param {File} file
27002 * Fire when xhr load exception
27003 * @param {Roo.bootstrap.DocumentManager} this
27004 * @param {XMLHttpRequest} xhr
27006 "exception" : true,
27009 * prepare the form data
27010 * @param {Roo.bootstrap.DocumentManager} this
27011 * @param {Object} formData
27016 * Fire when remove the file
27017 * @param {Roo.bootstrap.DocumentManager} this
27018 * @param {Object} file
27023 * Fire after refresh the file
27024 * @param {Roo.bootstrap.DocumentManager} this
27029 * Fire after click the image
27030 * @param {Roo.bootstrap.DocumentManager} this
27031 * @param {Object} file
27036 * Fire when upload a image and editable set to true
27037 * @param {Roo.bootstrap.DocumentManager} this
27038 * @param {Object} file
27042 * @event beforeselectfile
27043 * Fire before select file
27044 * @param {Roo.bootstrap.DocumentManager} this
27046 "beforeselectfile" : true,
27049 * Fire before process file
27050 * @param {Roo.bootstrap.DocumentManager} this
27051 * @param {Object} file
27058 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27067 paramName : 'imageUpload',
27070 labelAlign : 'left',
27077 getAutoCreate : function()
27079 var managerWidget = {
27081 cls : 'roo-document-manager',
27085 cls : 'roo-document-manager-selector',
27090 cls : 'roo-document-manager-uploader',
27094 cls : 'roo-document-manager-upload-btn',
27095 html : '<i class="fa fa-plus"></i>'
27106 cls : 'column col-md-12',
27111 if(this.fieldLabel.length){
27116 cls : 'column col-md-12',
27117 html : this.fieldLabel
27121 cls : 'column col-md-12',
27126 if(this.labelAlign == 'left'){
27130 cls : 'column col-md-' + this.labelWidth,
27131 html : this.fieldLabel
27135 cls : 'column col-md-' + (12 - this.labelWidth),
27145 cls : 'row clearfix',
27153 initEvents : function()
27155 this.managerEl = this.el.select('.roo-document-manager', true).first();
27156 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27158 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27159 this.selectorEl.hide();
27162 this.selectorEl.attr('multiple', 'multiple');
27165 this.selectorEl.on('change', this.onFileSelected, this);
27167 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27168 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27170 this.uploader.on('click', this.onUploaderClick, this);
27172 this.renderProgressDialog();
27176 window.addEventListener("resize", function() { _this.refresh(); } );
27178 this.fireEvent('initial', this);
27181 renderProgressDialog : function()
27185 this.progressDialog = new Roo.bootstrap.Modal({
27186 cls : 'roo-document-manager-progress-dialog',
27187 allow_close : false,
27197 btnclick : function() {
27198 _this.uploadCancel();
27204 this.progressDialog.render(Roo.get(document.body));
27206 this.progress = new Roo.bootstrap.Progress({
27207 cls : 'roo-document-manager-progress',
27212 this.progress.render(this.progressDialog.getChildContainer());
27214 this.progressBar = new Roo.bootstrap.ProgressBar({
27215 cls : 'roo-document-manager-progress-bar',
27218 aria_valuemax : 12,
27222 this.progressBar.render(this.progress.getChildContainer());
27225 onUploaderClick : function(e)
27227 e.preventDefault();
27229 if(this.fireEvent('beforeselectfile', this) != false){
27230 this.selectorEl.dom.click();
27235 onFileSelected : function(e)
27237 e.preventDefault();
27239 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27243 Roo.each(this.selectorEl.dom.files, function(file){
27244 if(this.fireEvent('inspect', this, file) != false){
27245 this.files.push(file);
27255 this.selectorEl.dom.value = '';
27257 if(!this.files.length){
27261 if(this.boxes > 0 && this.files.length > this.boxes){
27262 this.files = this.files.slice(0, this.boxes);
27265 this.uploader.show();
27267 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27268 this.uploader.hide();
27277 Roo.each(this.files, function(file){
27279 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27280 var f = this.renderPreview(file);
27285 if(file.type.indexOf('image') != -1){
27286 this.delegates.push(
27288 _this.process(file);
27289 }).createDelegate(this)
27297 _this.process(file);
27298 }).createDelegate(this)
27303 this.files = files;
27305 this.delegates = this.delegates.concat(docs);
27307 if(!this.delegates.length){
27312 this.progressBar.aria_valuemax = this.delegates.length;
27319 arrange : function()
27321 if(!this.delegates.length){
27322 this.progressDialog.hide();
27327 var delegate = this.delegates.shift();
27329 this.progressDialog.show();
27331 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27333 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27338 refresh : function()
27340 this.uploader.show();
27342 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27343 this.uploader.hide();
27346 Roo.isTouch ? this.closable(false) : this.closable(true);
27348 this.fireEvent('refresh', this);
27351 onRemove : function(e, el, o)
27353 e.preventDefault();
27355 this.fireEvent('remove', this, o);
27359 remove : function(o)
27363 Roo.each(this.files, function(file){
27364 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27373 this.files = files;
27380 Roo.each(this.files, function(file){
27385 file.target.remove();
27394 onClick : function(e, el, o)
27396 e.preventDefault();
27398 this.fireEvent('click', this, o);
27402 closable : function(closable)
27404 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27406 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27418 xhrOnLoad : function(xhr)
27420 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27424 if (xhr.readyState !== 4) {
27426 this.fireEvent('exception', this, xhr);
27430 var response = Roo.decode(xhr.responseText);
27432 if(!response.success){
27434 this.fireEvent('exception', this, xhr);
27438 var file = this.renderPreview(response.data);
27440 this.files.push(file);
27446 xhrOnError : function(xhr)
27448 Roo.log('xhr on error');
27450 var response = Roo.decode(xhr.responseText);
27457 process : function(file)
27459 if(this.fireEvent('process', this, file) !== false){
27460 if(this.editable && file.type.indexOf('image') != -1){
27461 this.fireEvent('edit', this, file);
27465 this.uploadStart(file, false);
27472 uploadStart : function(file, crop)
27474 this.xhr = new XMLHttpRequest();
27476 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27481 file.xhr = this.xhr;
27483 this.managerEl.createChild({
27485 cls : 'roo-document-manager-loading',
27489 tooltip : file.name,
27490 cls : 'roo-document-manager-thumb',
27491 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27497 this.xhr.open(this.method, this.url, true);
27500 "Accept": "application/json",
27501 "Cache-Control": "no-cache",
27502 "X-Requested-With": "XMLHttpRequest"
27505 for (var headerName in headers) {
27506 var headerValue = headers[headerName];
27508 this.xhr.setRequestHeader(headerName, headerValue);
27514 this.xhr.onload = function()
27516 _this.xhrOnLoad(_this.xhr);
27519 this.xhr.onerror = function()
27521 _this.xhrOnError(_this.xhr);
27524 var formData = new FormData();
27526 formData.append('returnHTML', 'NO');
27529 formData.append('crop', crop);
27532 formData.append(this.paramName, file, file.name);
27534 if(this.fireEvent('prepare', this, formData) != false){
27535 this.xhr.send(formData);
27539 uploadCancel : function()
27546 this.delegates = [];
27548 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27555 renderPreview : function(file)
27557 if(typeof(file.target) != 'undefined' && file.target){
27561 var previewEl = this.managerEl.createChild({
27563 cls : 'roo-document-manager-preview',
27567 tooltip : file.filename,
27568 cls : 'roo-document-manager-thumb',
27569 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27574 html : '<i class="fa fa-times-circle"></i>'
27579 var close = previewEl.select('button.close', true).first();
27581 close.on('click', this.onRemove, this, file);
27583 file.target = previewEl;
27585 var image = previewEl.select('img', true).first();
27589 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27591 image.on('click', this.onClick, this, file);
27597 onPreviewLoad : function(file, image)
27599 if(typeof(file.target) == 'undefined' || !file.target){
27603 var width = image.dom.naturalWidth || image.dom.width;
27604 var height = image.dom.naturalHeight || image.dom.height;
27606 if(width > height){
27607 file.target.addClass('wide');
27611 file.target.addClass('tall');
27616 uploadFromSource : function(file, crop)
27618 this.xhr = new XMLHttpRequest();
27620 this.managerEl.createChild({
27622 cls : 'roo-document-manager-loading',
27626 tooltip : file.name,
27627 cls : 'roo-document-manager-thumb',
27628 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27634 this.xhr.open(this.method, this.url, true);
27637 "Accept": "application/json",
27638 "Cache-Control": "no-cache",
27639 "X-Requested-With": "XMLHttpRequest"
27642 for (var headerName in headers) {
27643 var headerValue = headers[headerName];
27645 this.xhr.setRequestHeader(headerName, headerValue);
27651 this.xhr.onload = function()
27653 _this.xhrOnLoad(_this.xhr);
27656 this.xhr.onerror = function()
27658 _this.xhrOnError(_this.xhr);
27661 var formData = new FormData();
27663 formData.append('returnHTML', 'NO');
27665 formData.append('crop', crop);
27667 if(typeof(file.filename) != 'undefined'){
27668 formData.append('filename', file.filename);
27671 if(typeof(file.mimetype) != 'undefined'){
27672 formData.append('mimetype', file.mimetype);
27675 if(this.fireEvent('prepare', this, formData) != false){
27676 this.xhr.send(formData);
27686 * @class Roo.bootstrap.DocumentViewer
27687 * @extends Roo.bootstrap.Component
27688 * Bootstrap DocumentViewer class
27691 * Create a new DocumentViewer
27692 * @param {Object} config The config object
27695 Roo.bootstrap.DocumentViewer = function(config){
27696 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27701 * Fire after initEvent
27702 * @param {Roo.bootstrap.DocumentViewer} this
27708 * @param {Roo.bootstrap.DocumentViewer} this
27713 * Fire after trash button
27714 * @param {Roo.bootstrap.DocumentViewer} this
27721 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27723 getAutoCreate : function()
27727 cls : 'roo-document-viewer',
27731 cls : 'roo-document-viewer-body',
27735 cls : 'roo-document-viewer-thumb',
27739 cls : 'roo-document-viewer-image'
27747 cls : 'roo-document-viewer-footer',
27750 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27758 cls : 'btn btn-default roo-document-viewer-trash',
27759 html : '<i class="fa fa-trash"></i>'
27772 initEvents : function()
27775 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27776 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27778 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27779 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27781 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27782 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27784 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27785 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27787 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27788 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27790 this.bodyEl.on('click', this.onClick, this);
27792 this.trashBtn.on('click', this.onTrash, this);
27796 initial : function()
27798 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27801 this.fireEvent('initial', this);
27805 onClick : function(e)
27807 e.preventDefault();
27809 this.fireEvent('click', this);
27812 onTrash : function(e)
27814 e.preventDefault();
27816 this.fireEvent('trash', this);
27828 * @class Roo.bootstrap.NavProgressBar
27829 * @extends Roo.bootstrap.Component
27830 * Bootstrap NavProgressBar class
27833 * Create a new nav progress bar
27834 * @param {Object} config The config object
27837 Roo.bootstrap.NavProgressBar = function(config){
27838 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27840 this.bullets = this.bullets || [];
27842 // Roo.bootstrap.NavProgressBar.register(this);
27846 * Fires when the active item changes
27847 * @param {Roo.bootstrap.NavProgressBar} this
27848 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27849 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27856 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27861 getAutoCreate : function()
27863 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27867 cls : 'roo-navigation-bar-group',
27871 cls : 'roo-navigation-top-bar'
27875 cls : 'roo-navigation-bullets-bar',
27879 cls : 'roo-navigation-bar'
27886 cls : 'roo-navigation-bottom-bar'
27896 initEvents: function()
27901 onRender : function(ct, position)
27903 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27905 if(this.bullets.length){
27906 Roo.each(this.bullets, function(b){
27915 addItem : function(cfg)
27917 var item = new Roo.bootstrap.NavProgressItem(cfg);
27919 item.parentId = this.id;
27920 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27923 var top = new Roo.bootstrap.Element({
27925 cls : 'roo-navigation-bar-text'
27928 var bottom = new Roo.bootstrap.Element({
27930 cls : 'roo-navigation-bar-text'
27933 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27934 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27936 var topText = new Roo.bootstrap.Element({
27938 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27941 var bottomText = new Roo.bootstrap.Element({
27943 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27946 topText.onRender(top.el, null);
27947 bottomText.onRender(bottom.el, null);
27950 item.bottomEl = bottom;
27953 this.barItems.push(item);
27958 getActive : function()
27960 var active = false;
27962 Roo.each(this.barItems, function(v){
27964 if (!v.isActive()) {
27976 setActiveItem : function(item)
27980 Roo.each(this.barItems, function(v){
27981 if (v.rid == item.rid) {
27985 if (v.isActive()) {
27986 v.setActive(false);
27991 item.setActive(true);
27993 this.fireEvent('changed', this, item, prev);
27996 getBarItem: function(rid)
28000 Roo.each(this.barItems, function(e) {
28001 if (e.rid != rid) {
28012 indexOfItem : function(item)
28016 Roo.each(this.barItems, function(v, i){
28018 if (v.rid != item.rid) {
28029 setActiveNext : function()
28031 var i = this.indexOfItem(this.getActive());
28033 if (i > this.barItems.length) {
28037 this.setActiveItem(this.barItems[i+1]);
28040 setActivePrev : function()
28042 var i = this.indexOfItem(this.getActive());
28048 this.setActiveItem(this.barItems[i-1]);
28051 format : function()
28053 if(!this.barItems.length){
28057 var width = 100 / this.barItems.length;
28059 Roo.each(this.barItems, function(i){
28060 i.el.setStyle('width', width + '%');
28061 i.topEl.el.setStyle('width', width + '%');
28062 i.bottomEl.el.setStyle('width', width + '%');
28071 * Nav Progress Item
28076 * @class Roo.bootstrap.NavProgressItem
28077 * @extends Roo.bootstrap.Component
28078 * Bootstrap NavProgressItem class
28079 * @cfg {String} rid the reference id
28080 * @cfg {Boolean} active (true|false) Is item active default false
28081 * @cfg {Boolean} disabled (true|false) Is item active default false
28082 * @cfg {String} html
28083 * @cfg {String} position (top|bottom) text position default bottom
28084 * @cfg {String} icon show icon instead of number
28087 * Create a new NavProgressItem
28088 * @param {Object} config The config object
28090 Roo.bootstrap.NavProgressItem = function(config){
28091 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28096 * The raw click event for the entire grid.
28097 * @param {Roo.bootstrap.NavProgressItem} this
28098 * @param {Roo.EventObject} e
28105 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28111 position : 'bottom',
28114 getAutoCreate : function()
28116 var iconCls = 'roo-navigation-bar-item-icon';
28118 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28122 cls: 'roo-navigation-bar-item',
28132 cfg.cls += ' active';
28135 cfg.cls += ' disabled';
28141 disable : function()
28143 this.setDisabled(true);
28146 enable : function()
28148 this.setDisabled(false);
28151 initEvents: function()
28153 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28155 this.iconEl.on('click', this.onClick, this);
28158 onClick : function(e)
28160 e.preventDefault();
28166 if(this.fireEvent('click', this, e) === false){
28170 this.parent().setActiveItem(this);
28173 isActive: function ()
28175 return this.active;
28178 setActive : function(state)
28180 if(this.active == state){
28184 this.active = state;
28187 this.el.addClass('active');
28191 this.el.removeClass('active');
28196 setDisabled : function(state)
28198 if(this.disabled == state){
28202 this.disabled = state;
28205 this.el.addClass('disabled');
28209 this.el.removeClass('disabled');
28212 tooltipEl : function()
28214 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28227 * @class Roo.bootstrap.FieldLabel
28228 * @extends Roo.bootstrap.Component
28229 * Bootstrap FieldLabel class
28230 * @cfg {String} html contents of the element
28231 * @cfg {String} tag tag of the element default label
28232 * @cfg {String} cls class of the element
28233 * @cfg {String} target label target
28234 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28235 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28236 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28237 * @cfg {String} iconTooltip default "This field is required"
28240 * Create a new FieldLabel
28241 * @param {Object} config The config object
28244 Roo.bootstrap.FieldLabel = function(config){
28245 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28250 * Fires after the field has been marked as invalid.
28251 * @param {Roo.form.FieldLabel} this
28252 * @param {String} msg The validation message
28257 * Fires after the field has been validated with no errors.
28258 * @param {Roo.form.FieldLabel} this
28264 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28271 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28272 validClass : 'text-success fa fa-lg fa-check',
28273 iconTooltip : 'This field is required',
28275 getAutoCreate : function(){
28279 cls : 'roo-bootstrap-field-label ' + this.cls,
28285 tooltip : this.iconTooltip
28297 initEvents: function()
28299 Roo.bootstrap.Element.superclass.initEvents.call(this);
28301 this.iconEl = this.el.select('i', true).first();
28303 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28305 Roo.bootstrap.FieldLabel.register(this);
28309 * Mark this field as valid
28311 markValid : function()
28313 this.iconEl.show();
28315 this.iconEl.removeClass(this.invalidClass);
28317 this.iconEl.addClass(this.validClass);
28319 this.fireEvent('valid', this);
28323 * Mark this field as invalid
28324 * @param {String} msg The validation message
28326 markInvalid : function(msg)
28328 this.iconEl.show();
28330 this.iconEl.removeClass(this.validClass);
28332 this.iconEl.addClass(this.invalidClass);
28334 this.fireEvent('invalid', this, msg);
28340 Roo.apply(Roo.bootstrap.FieldLabel, {
28345 * register a FieldLabel Group
28346 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28348 register : function(label)
28350 if(this.groups.hasOwnProperty(label.target)){
28354 this.groups[label.target] = label;
28358 * fetch a FieldLabel Group based on the target
28359 * @param {string} target
28360 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28362 get: function(target) {
28363 if (typeof(this.groups[target]) == 'undefined') {
28367 return this.groups[target] ;
28376 * page DateSplitField.
28382 * @class Roo.bootstrap.DateSplitField
28383 * @extends Roo.bootstrap.Component
28384 * Bootstrap DateSplitField class
28385 * @cfg {string} fieldLabel - the label associated
28386 * @cfg {Number} labelWidth set the width of label (0-12)
28387 * @cfg {String} labelAlign (top|left)
28388 * @cfg {Boolean} dayAllowBlank (true|false) default false
28389 * @cfg {Boolean} monthAllowBlank (true|false) default false
28390 * @cfg {Boolean} yearAllowBlank (true|false) default false
28391 * @cfg {string} dayPlaceholder
28392 * @cfg {string} monthPlaceholder
28393 * @cfg {string} yearPlaceholder
28394 * @cfg {string} dayFormat default 'd'
28395 * @cfg {string} monthFormat default 'm'
28396 * @cfg {string} yearFormat default 'Y'
28400 * Create a new DateSplitField
28401 * @param {Object} config The config object
28404 Roo.bootstrap.DateSplitField = function(config){
28405 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28411 * getting the data of years
28412 * @param {Roo.bootstrap.DateSplitField} this
28413 * @param {Object} years
28418 * getting the data of days
28419 * @param {Roo.bootstrap.DateSplitField} this
28420 * @param {Object} days
28425 * Fires after the field has been marked as invalid.
28426 * @param {Roo.form.Field} this
28427 * @param {String} msg The validation message
28432 * Fires after the field has been validated with no errors.
28433 * @param {Roo.form.Field} this
28439 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28442 labelAlign : 'top',
28444 dayAllowBlank : false,
28445 monthAllowBlank : false,
28446 yearAllowBlank : false,
28447 dayPlaceholder : '',
28448 monthPlaceholder : '',
28449 yearPlaceholder : '',
28453 isFormField : true,
28455 getAutoCreate : function()
28459 cls : 'row roo-date-split-field-group',
28464 cls : 'form-hidden-field roo-date-split-field-group-value',
28470 if(this.fieldLabel){
28473 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28477 html : this.fieldLabel
28483 Roo.each(['day', 'month', 'year'], function(t){
28486 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28493 inputEl: function ()
28495 return this.el.select('.roo-date-split-field-group-value', true).first();
28498 onRender : function(ct, position)
28502 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28504 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28506 this.dayField = new Roo.bootstrap.ComboBox({
28507 allowBlank : this.dayAllowBlank,
28508 alwaysQuery : true,
28509 displayField : 'value',
28512 forceSelection : true,
28514 placeholder : this.dayPlaceholder,
28515 selectOnFocus : true,
28516 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28517 triggerAction : 'all',
28519 valueField : 'value',
28520 store : new Roo.data.SimpleStore({
28521 data : (function() {
28523 _this.fireEvent('days', _this, days);
28526 fields : [ 'value' ]
28529 select : function (_self, record, index)
28531 _this.setValue(_this.getValue());
28536 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28538 this.monthField = new Roo.bootstrap.MonthField({
28539 after : '<i class=\"fa fa-calendar\"></i>',
28540 allowBlank : this.monthAllowBlank,
28541 placeholder : this.monthPlaceholder,
28544 render : function (_self)
28546 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28547 e.preventDefault();
28551 select : function (_self, oldvalue, newvalue)
28553 _this.setValue(_this.getValue());
28558 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28560 this.yearField = new Roo.bootstrap.ComboBox({
28561 allowBlank : this.yearAllowBlank,
28562 alwaysQuery : true,
28563 displayField : 'value',
28566 forceSelection : true,
28568 placeholder : this.yearPlaceholder,
28569 selectOnFocus : true,
28570 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28571 triggerAction : 'all',
28573 valueField : 'value',
28574 store : new Roo.data.SimpleStore({
28575 data : (function() {
28577 _this.fireEvent('years', _this, years);
28580 fields : [ 'value' ]
28583 select : function (_self, record, index)
28585 _this.setValue(_this.getValue());
28590 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28593 setValue : function(v, format)
28595 this.inputEl.dom.value = v;
28597 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28599 var d = Date.parseDate(v, f);
28606 this.setDay(d.format(this.dayFormat));
28607 this.setMonth(d.format(this.monthFormat));
28608 this.setYear(d.format(this.yearFormat));
28615 setDay : function(v)
28617 this.dayField.setValue(v);
28618 this.inputEl.dom.value = this.getValue();
28623 setMonth : function(v)
28625 this.monthField.setValue(v, true);
28626 this.inputEl.dom.value = this.getValue();
28631 setYear : function(v)
28633 this.yearField.setValue(v);
28634 this.inputEl.dom.value = this.getValue();
28639 getDay : function()
28641 return this.dayField.getValue();
28644 getMonth : function()
28646 return this.monthField.getValue();
28649 getYear : function()
28651 return this.yearField.getValue();
28654 getValue : function()
28656 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28658 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28668 this.inputEl.dom.value = '';
28673 validate : function()
28675 var d = this.dayField.validate();
28676 var m = this.monthField.validate();
28677 var y = this.yearField.validate();
28682 (!this.dayAllowBlank && !d) ||
28683 (!this.monthAllowBlank && !m) ||
28684 (!this.yearAllowBlank && !y)
28689 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28698 this.markInvalid();
28703 markValid : function()
28706 var label = this.el.select('label', true).first();
28707 var icon = this.el.select('i.fa-star', true).first();
28713 this.fireEvent('valid', this);
28717 * Mark this field as invalid
28718 * @param {String} msg The validation message
28720 markInvalid : function(msg)
28723 var label = this.el.select('label', true).first();
28724 var icon = this.el.select('i.fa-star', true).first();
28726 if(label && !icon){
28727 this.el.select('.roo-date-split-field-label', true).createChild({
28729 cls : 'text-danger fa fa-lg fa-star',
28730 tooltip : 'This field is required',
28731 style : 'margin-right:5px;'
28735 this.fireEvent('invalid', this, msg);
28738 clearInvalid : function()
28740 var label = this.el.select('label', true).first();
28741 var icon = this.el.select('i.fa-star', true).first();
28747 this.fireEvent('valid', this);
28750 getName: function()
28760 * http://masonry.desandro.com
28762 * The idea is to render all the bricks based on vertical width...
28764 * The original code extends 'outlayer' - we might need to use that....
28770 * @class Roo.bootstrap.LayoutMasonry
28771 * @extends Roo.bootstrap.Component
28772 * Bootstrap Layout Masonry class
28775 * Create a new Element
28776 * @param {Object} config The config object
28779 Roo.bootstrap.LayoutMasonry = function(config){
28780 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28786 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
28789 * @cfg {Boolean} isLayoutInstant = no animation?
28791 isLayoutInstant : false, // needed?
28794 * @cfg {Number} boxWidth width of the columns
28799 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
28804 * @cfg {Number} padWidth padding below box..
28809 * @cfg {Number} gutter gutter width..
28814 * @cfg {Number} maxCols maximum number of columns
28820 * @cfg {Boolean} isAutoInitial defalut true
28822 isAutoInitial : true,
28827 * @cfg {Boolean} isHorizontal defalut false
28829 isHorizontal : false,
28831 currentSize : null,
28837 bricks: null, //CompositeElement
28841 _isLayoutInited : false,
28843 // isAlternative : false, // only use for vertical layout...
28846 * @cfg {Number} alternativePadWidth padding below box..
28848 alternativePadWidth : 50,
28850 getAutoCreate : function(){
28854 cls: 'blog-masonary-wrapper ' + this.cls,
28856 cls : 'mas-boxes masonary'
28863 getChildContainer: function( )
28865 if (this.boxesEl) {
28866 return this.boxesEl;
28869 this.boxesEl = this.el.select('.mas-boxes').first();
28871 return this.boxesEl;
28875 initEvents : function()
28879 if(this.isAutoInitial){
28880 Roo.log('hook children rendered');
28881 this.on('childrenrendered', function() {
28882 Roo.log('children rendered');
28888 initial : function()
28890 this.currentSize = this.el.getBox(true);
28892 Roo.EventManager.onWindowResize(this.resize, this);
28894 if(!this.isAutoInitial){
28902 //this.layout.defer(500,this);
28906 resize : function()
28910 var cs = this.el.getBox(true);
28912 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28913 Roo.log("no change in with or X");
28917 this.currentSize = cs;
28923 layout : function()
28925 this._resetLayout();
28927 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28929 this.layoutItems( isInstant );
28931 this._isLayoutInited = true;
28935 _resetLayout : function()
28937 if(this.isHorizontal){
28938 this.horizontalMeasureColumns();
28942 this.verticalMeasureColumns();
28946 verticalMeasureColumns : function()
28948 this.getContainerWidth();
28950 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28951 // this.colWidth = Math.floor(this.containerWidth * 0.8);
28955 var boxWidth = this.boxWidth + this.padWidth;
28957 if(this.containerWidth < this.boxWidth){
28958 boxWidth = this.containerWidth
28961 var containerWidth = this.containerWidth;
28963 var cols = Math.floor(containerWidth / boxWidth);
28965 this.cols = Math.max( cols, 1 );
28967 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28969 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28971 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28973 this.colWidth = boxWidth + avail - this.padWidth;
28975 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28976 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
28979 horizontalMeasureColumns : function()
28981 this.getContainerWidth();
28983 var boxWidth = this.boxWidth;
28985 if(this.containerWidth < boxWidth){
28986 boxWidth = this.containerWidth;
28989 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28991 this.el.setHeight(boxWidth);
28995 getContainerWidth : function()
28997 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29000 layoutItems : function( isInstant )
29002 var items = Roo.apply([], this.bricks);
29004 if(this.isHorizontal){
29005 this._horizontalLayoutItems( items , isInstant );
29009 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29010 // this._verticalAlternativeLayoutItems( items , isInstant );
29014 this._verticalLayoutItems( items , isInstant );
29018 _verticalLayoutItems : function ( items , isInstant)
29020 if ( !items || !items.length ) {
29025 ['xs', 'xs', 'xs', 'tall'],
29026 ['xs', 'xs', 'tall'],
29027 ['xs', 'xs', 'sm'],
29028 ['xs', 'xs', 'xs'],
29034 ['sm', 'xs', 'xs'],
29038 ['tall', 'xs', 'xs', 'xs'],
29039 ['tall', 'xs', 'xs'],
29051 Roo.each(items, function(item, k){
29053 switch (item.size) {
29054 // these layouts take up a full box,
29065 boxes.push([item]);
29088 var filterPattern = function(box, length)
29096 var pattern = box.slice(0, length);
29100 Roo.each(pattern, function(i){
29101 format.push(i.size);
29104 Roo.each(standard, function(s){
29106 if(String(s) != String(format)){
29115 if(!match && length == 1){
29120 filterPattern(box, length - 1);
29124 queue.push(pattern);
29126 box = box.slice(length, box.length);
29128 filterPattern(box, 4);
29134 Roo.each(boxes, function(box, k){
29140 if(box.length == 1){
29145 filterPattern(box, 4);
29149 this._processVerticalLayoutQueue( queue, isInstant );
29153 // _verticalAlternativeLayoutItems : function( items , isInstant )
29155 // if ( !items || !items.length ) {
29159 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29163 _horizontalLayoutItems : function ( items , isInstant)
29165 if ( !items || !items.length || items.length < 3) {
29171 var eItems = items.slice(0, 3);
29173 items = items.slice(3, items.length);
29176 ['xs', 'xs', 'xs', 'wide'],
29177 ['xs', 'xs', 'wide'],
29178 ['xs', 'xs', 'sm'],
29179 ['xs', 'xs', 'xs'],
29185 ['sm', 'xs', 'xs'],
29189 ['wide', 'xs', 'xs', 'xs'],
29190 ['wide', 'xs', 'xs'],
29203 Roo.each(items, function(item, k){
29205 switch (item.size) {
29216 boxes.push([item]);
29240 var filterPattern = function(box, length)
29248 var pattern = box.slice(0, length);
29252 Roo.each(pattern, function(i){
29253 format.push(i.size);
29256 Roo.each(standard, function(s){
29258 if(String(s) != String(format)){
29267 if(!match && length == 1){
29272 filterPattern(box, length - 1);
29276 queue.push(pattern);
29278 box = box.slice(length, box.length);
29280 filterPattern(box, 4);
29286 Roo.each(boxes, function(box, k){
29292 if(box.length == 1){
29297 filterPattern(box, 4);
29304 var pos = this.el.getBox(true);
29308 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29310 var hit_end = false;
29312 Roo.each(queue, function(box){
29316 Roo.each(box, function(b){
29318 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29328 Roo.each(box, function(b){
29330 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29333 mx = Math.max(mx, b.x);
29337 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29341 Roo.each(box, function(b){
29343 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29357 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29360 /** Sets position of item in DOM
29361 * @param {Element} item
29362 * @param {Number} x - horizontal position
29363 * @param {Number} y - vertical position
29364 * @param {Boolean} isInstant - disables transitions
29366 _processVerticalLayoutQueue : function( queue, isInstant )
29368 var pos = this.el.getBox(true);
29373 for (var i = 0; i < this.cols; i++){
29377 Roo.each(queue, function(box, k){
29379 var col = k % this.cols;
29381 Roo.each(box, function(b,kk){
29383 b.el.position('absolute');
29385 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29386 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29388 if(b.size == 'md-left' || b.size == 'md-right'){
29389 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29390 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29393 b.el.setWidth(width);
29394 b.el.setHeight(height);
29396 b.el.select('iframe',true).setSize(width,height);
29400 for (var i = 0; i < this.cols; i++){
29402 if(maxY[i] < maxY[col]){
29407 col = Math.min(col, i);
29411 x = pos.x + col * (this.colWidth + this.padWidth);
29415 var positions = [];
29417 switch (box.length){
29419 positions = this.getVerticalOneBoxColPositions(x, y, box);
29422 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29425 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29428 positions = this.getVerticalFourBoxColPositions(x, y, box);
29434 Roo.each(box, function(b,kk){
29436 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29438 var sz = b.el.getSize();
29440 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29448 for (var i = 0; i < this.cols; i++){
29449 mY = Math.max(mY, maxY[i]);
29452 this.el.setHeight(mY - pos.y);
29456 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29458 // var pos = this.el.getBox(true);
29461 // var maxX = pos.right;
29463 // var maxHeight = 0;
29465 // Roo.each(items, function(item, k){
29469 // item.el.position('absolute');
29471 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29473 // item.el.setWidth(width);
29475 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29477 // item.el.setHeight(height);
29480 // item.el.setXY([x, y], isInstant ? false : true);
29482 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29485 // y = y + height + this.alternativePadWidth;
29487 // maxHeight = maxHeight + height + this.alternativePadWidth;
29491 // this.el.setHeight(maxHeight);
29495 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29497 var pos = this.el.getBox(true);
29502 var maxX = pos.right;
29504 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29506 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29508 Roo.each(queue, function(box, k){
29510 Roo.each(box, function(b, kk){
29512 b.el.position('absolute');
29514 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29515 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29517 if(b.size == 'md-left' || b.size == 'md-right'){
29518 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29519 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29522 b.el.setWidth(width);
29523 b.el.setHeight(height);
29531 var positions = [];
29533 switch (box.length){
29535 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29538 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29541 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29544 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29550 Roo.each(box, function(b,kk){
29552 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29554 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29562 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29564 Roo.each(eItems, function(b,k){
29566 b.size = (k == 0) ? 'sm' : 'xs';
29567 b.x = (k == 0) ? 2 : 1;
29568 b.y = (k == 0) ? 2 : 1;
29570 b.el.position('absolute');
29572 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29574 b.el.setWidth(width);
29576 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29578 b.el.setHeight(height);
29582 var positions = [];
29585 x : maxX - this.unitWidth * 2 - this.gutter,
29590 x : maxX - this.unitWidth,
29591 y : minY + (this.unitWidth + this.gutter) * 2
29595 x : maxX - this.unitWidth * 3 - this.gutter * 2,
29599 Roo.each(eItems, function(b,k){
29601 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29607 getVerticalOneBoxColPositions : function(x, y, box)
29611 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29613 if(box[0].size == 'md-left'){
29617 if(box[0].size == 'md-right'){
29622 x : x + (this.unitWidth + this.gutter) * rand,
29629 getVerticalTwoBoxColPositions : function(x, y, box)
29633 if(box[0].size == 'xs'){
29637 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29641 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29655 x : x + (this.unitWidth + this.gutter) * 2,
29656 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29663 getVerticalThreeBoxColPositions : function(x, y, box)
29667 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29675 x : x + (this.unitWidth + this.gutter) * 1,
29680 x : x + (this.unitWidth + this.gutter) * 2,
29688 if(box[0].size == 'xs' && box[1].size == 'xs'){
29697 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29701 x : x + (this.unitWidth + this.gutter) * 1,
29715 x : x + (this.unitWidth + this.gutter) * 2,
29720 x : x + (this.unitWidth + this.gutter) * 2,
29721 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29728 getVerticalFourBoxColPositions : function(x, y, box)
29732 if(box[0].size == 'xs'){
29741 y : y + (this.unitHeight + this.gutter) * 1
29746 y : y + (this.unitHeight + this.gutter) * 2
29750 x : x + (this.unitWidth + this.gutter) * 1,
29764 x : x + (this.unitWidth + this.gutter) * 2,
29769 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29770 y : y + (this.unitHeight + this.gutter) * 1
29774 x : x + (this.unitWidth + this.gutter) * 2,
29775 y : y + (this.unitWidth + this.gutter) * 2
29782 getHorizontalOneBoxColPositions : function(maxX, minY, box)
29786 if(box[0].size == 'md-left'){
29788 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29795 if(box[0].size == 'md-right'){
29797 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29798 y : minY + (this.unitWidth + this.gutter) * 1
29804 var rand = Math.floor(Math.random() * (4 - box[0].y));
29807 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29808 y : minY + (this.unitWidth + this.gutter) * rand
29815 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29819 if(box[0].size == 'xs'){
29822 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29827 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29828 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29836 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29841 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29842 y : minY + (this.unitWidth + this.gutter) * 2
29849 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29853 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29856 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29861 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29862 y : minY + (this.unitWidth + this.gutter) * 1
29866 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29867 y : minY + (this.unitWidth + this.gutter) * 2
29874 if(box[0].size == 'xs' && box[1].size == 'xs'){
29877 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29882 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29887 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29888 y : minY + (this.unitWidth + this.gutter) * 1
29896 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29901 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29902 y : minY + (this.unitWidth + this.gutter) * 2
29906 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29907 y : minY + (this.unitWidth + this.gutter) * 2
29914 getHorizontalFourBoxColPositions : function(maxX, minY, box)
29918 if(box[0].size == 'xs'){
29921 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29926 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29931 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),
29936 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29937 y : minY + (this.unitWidth + this.gutter) * 1
29945 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29950 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29951 y : minY + (this.unitWidth + this.gutter) * 2
29955 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29956 y : minY + (this.unitWidth + this.gutter) * 2
29960 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),
29961 y : minY + (this.unitWidth + this.gutter) * 2
29975 * http://masonry.desandro.com
29977 * The idea is to render all the bricks based on vertical width...
29979 * The original code extends 'outlayer' - we might need to use that....
29985 * @class Roo.bootstrap.LayoutMasonryAuto
29986 * @extends Roo.bootstrap.Component
29987 * Bootstrap Layout Masonry class
29990 * Create a new Element
29991 * @param {Object} config The config object
29994 Roo.bootstrap.LayoutMasonryAuto = function(config){
29995 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29998 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30001 * @cfg {Boolean} isFitWidth - resize the width..
30003 isFitWidth : false, // options..
30005 * @cfg {Boolean} isOriginLeft = left align?
30007 isOriginLeft : true,
30009 * @cfg {Boolean} isOriginTop = top align?
30011 isOriginTop : false,
30013 * @cfg {Boolean} isLayoutInstant = no animation?
30015 isLayoutInstant : false, // needed?
30017 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30019 isResizingContainer : true,
30021 * @cfg {Number} columnWidth width of the columns
30027 * @cfg {Number} maxCols maximum number of columns
30032 * @cfg {Number} padHeight padding below box..
30038 * @cfg {Boolean} isAutoInitial defalut true
30041 isAutoInitial : true,
30047 initialColumnWidth : 0,
30048 currentSize : null,
30050 colYs : null, // array.
30057 bricks: null, //CompositeElement
30058 cols : 0, // array?
30059 // element : null, // wrapped now this.el
30060 _isLayoutInited : null,
30063 getAutoCreate : function(){
30067 cls: 'blog-masonary-wrapper ' + this.cls,
30069 cls : 'mas-boxes masonary'
30076 getChildContainer: function( )
30078 if (this.boxesEl) {
30079 return this.boxesEl;
30082 this.boxesEl = this.el.select('.mas-boxes').first();
30084 return this.boxesEl;
30088 initEvents : function()
30092 if(this.isAutoInitial){
30093 Roo.log('hook children rendered');
30094 this.on('childrenrendered', function() {
30095 Roo.log('children rendered');
30102 initial : function()
30104 this.reloadItems();
30106 this.currentSize = this.el.getBox(true);
30108 /// was window resize... - let's see if this works..
30109 Roo.EventManager.onWindowResize(this.resize, this);
30111 if(!this.isAutoInitial){
30116 this.layout.defer(500,this);
30119 reloadItems: function()
30121 this.bricks = this.el.select('.masonry-brick', true);
30123 this.bricks.each(function(b) {
30124 //Roo.log(b.getSize());
30125 if (!b.attr('originalwidth')) {
30126 b.attr('originalwidth', b.getSize().width);
30131 Roo.log(this.bricks.elements.length);
30134 resize : function()
30137 var cs = this.el.getBox(true);
30139 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30140 Roo.log("no change in with or X");
30143 this.currentSize = cs;
30147 layout : function()
30150 this._resetLayout();
30151 //this._manageStamps();
30153 // don't animate first layout
30154 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30155 this.layoutItems( isInstant );
30157 // flag for initalized
30158 this._isLayoutInited = true;
30161 layoutItems : function( isInstant )
30163 //var items = this._getItemsForLayout( this.items );
30164 // original code supports filtering layout items.. we just ignore it..
30166 this._layoutItems( this.bricks , isInstant );
30168 this._postLayout();
30170 _layoutItems : function ( items , isInstant)
30172 //this.fireEvent( 'layout', this, items );
30175 if ( !items || !items.elements.length ) {
30176 // no items, emit event with empty array
30181 items.each(function(item) {
30182 Roo.log("layout item");
30184 // get x/y object from method
30185 var position = this._getItemLayoutPosition( item );
30187 position.item = item;
30188 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30189 queue.push( position );
30192 this._processLayoutQueue( queue );
30194 /** Sets position of item in DOM
30195 * @param {Element} item
30196 * @param {Number} x - horizontal position
30197 * @param {Number} y - vertical position
30198 * @param {Boolean} isInstant - disables transitions
30200 _processLayoutQueue : function( queue )
30202 for ( var i=0, len = queue.length; i < len; i++ ) {
30203 var obj = queue[i];
30204 obj.item.position('absolute');
30205 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30211 * Any logic you want to do after each layout,
30212 * i.e. size the container
30214 _postLayout : function()
30216 this.resizeContainer();
30219 resizeContainer : function()
30221 if ( !this.isResizingContainer ) {
30224 var size = this._getContainerSize();
30226 this.el.setSize(size.width,size.height);
30227 this.boxesEl.setSize(size.width,size.height);
30233 _resetLayout : function()
30235 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30236 this.colWidth = this.el.getWidth();
30237 //this.gutter = this.el.getWidth();
30239 this.measureColumns();
30245 this.colYs.push( 0 );
30251 measureColumns : function()
30253 this.getContainerWidth();
30254 // if columnWidth is 0, default to outerWidth of first item
30255 if ( !this.columnWidth ) {
30256 var firstItem = this.bricks.first();
30257 Roo.log(firstItem);
30258 this.columnWidth = this.containerWidth;
30259 if (firstItem && firstItem.attr('originalwidth') ) {
30260 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30262 // columnWidth fall back to item of first element
30263 Roo.log("set column width?");
30264 this.initialColumnWidth = this.columnWidth ;
30266 // if first elem has no width, default to size of container
30271 if (this.initialColumnWidth) {
30272 this.columnWidth = this.initialColumnWidth;
30277 // column width is fixed at the top - however if container width get's smaller we should
30280 // this bit calcs how man columns..
30282 var columnWidth = this.columnWidth += this.gutter;
30284 // calculate columns
30285 var containerWidth = this.containerWidth + this.gutter;
30287 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30288 // fix rounding errors, typically with gutters
30289 var excess = columnWidth - containerWidth % columnWidth;
30292 // if overshoot is less than a pixel, round up, otherwise floor it
30293 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30294 cols = Math[ mathMethod ]( cols );
30295 this.cols = Math.max( cols, 1 );
30296 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30298 // padding positioning..
30299 var totalColWidth = this.cols * this.columnWidth;
30300 var padavail = this.containerWidth - totalColWidth;
30301 // so for 2 columns - we need 3 'pads'
30303 var padNeeded = (1+this.cols) * this.padWidth;
30305 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30307 this.columnWidth += padExtra
30308 //this.padWidth = Math.floor(padavail / ( this.cols));
30310 // adjust colum width so that padding is fixed??
30312 // we have 3 columns ... total = width * 3
30313 // we have X left over... that should be used by
30315 //if (this.expandC) {
30323 getContainerWidth : function()
30325 /* // container is parent if fit width
30326 var container = this.isFitWidth ? this.element.parentNode : this.element;
30327 // check that this.size and size are there
30328 // IE8 triggers resize on body size change, so they might not be
30330 var size = getSize( container ); //FIXME
30331 this.containerWidth = size && size.innerWidth; //FIXME
30334 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30338 _getItemLayoutPosition : function( item ) // what is item?
30340 // we resize the item to our columnWidth..
30342 item.setWidth(this.columnWidth);
30343 item.autoBoxAdjust = false;
30345 var sz = item.getSize();
30347 // how many columns does this brick span
30348 var remainder = this.containerWidth % this.columnWidth;
30350 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30351 // round if off by 1 pixel, otherwise use ceil
30352 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30353 colSpan = Math.min( colSpan, this.cols );
30355 // normally this should be '1' as we dont' currently allow multi width columns..
30357 var colGroup = this._getColGroup( colSpan );
30358 // get the minimum Y value from the columns
30359 var minimumY = Math.min.apply( Math, colGroup );
30360 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30362 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30364 // position the brick
30366 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30367 y: this.currentSize.y + minimumY + this.padHeight
30371 // apply setHeight to necessary columns
30372 var setHeight = minimumY + sz.height + this.padHeight;
30373 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30375 var setSpan = this.cols + 1 - colGroup.length;
30376 for ( var i = 0; i < setSpan; i++ ) {
30377 this.colYs[ shortColIndex + i ] = setHeight ;
30384 * @param {Number} colSpan - number of columns the element spans
30385 * @returns {Array} colGroup
30387 _getColGroup : function( colSpan )
30389 if ( colSpan < 2 ) {
30390 // if brick spans only one column, use all the column Ys
30395 // how many different places could this brick fit horizontally
30396 var groupCount = this.cols + 1 - colSpan;
30397 // for each group potential horizontal position
30398 for ( var i = 0; i < groupCount; i++ ) {
30399 // make an array of colY values for that one group
30400 var groupColYs = this.colYs.slice( i, i + colSpan );
30401 // and get the max value of the array
30402 colGroup[i] = Math.max.apply( Math, groupColYs );
30407 _manageStamp : function( stamp )
30409 var stampSize = stamp.getSize();
30410 var offset = stamp.getBox();
30411 // get the columns that this stamp affects
30412 var firstX = this.isOriginLeft ? offset.x : offset.right;
30413 var lastX = firstX + stampSize.width;
30414 var firstCol = Math.floor( firstX / this.columnWidth );
30415 firstCol = Math.max( 0, firstCol );
30417 var lastCol = Math.floor( lastX / this.columnWidth );
30418 // lastCol should not go over if multiple of columnWidth #425
30419 lastCol -= lastX % this.columnWidth ? 0 : 1;
30420 lastCol = Math.min( this.cols - 1, lastCol );
30422 // set colYs to bottom of the stamp
30423 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30426 for ( var i = firstCol; i <= lastCol; i++ ) {
30427 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30432 _getContainerSize : function()
30434 this.maxY = Math.max.apply( Math, this.colYs );
30439 if ( this.isFitWidth ) {
30440 size.width = this._getContainerFitWidth();
30446 _getContainerFitWidth : function()
30448 var unusedCols = 0;
30449 // count unused columns
30452 if ( this.colYs[i] !== 0 ) {
30457 // fit container to columns that have been used
30458 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30461 needsResizeLayout : function()
30463 var previousWidth = this.containerWidth;
30464 this.getContainerWidth();
30465 return previousWidth !== this.containerWidth;
30480 * @class Roo.bootstrap.MasonryBrick
30481 * @extends Roo.bootstrap.Component
30482 * Bootstrap MasonryBrick class
30485 * Create a new MasonryBrick
30486 * @param {Object} config The config object
30489 Roo.bootstrap.MasonryBrick = function(config){
30490 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30496 * When a MasonryBrick is clcik
30497 * @param {Roo.bootstrap.MasonryBrick} this
30498 * @param {Roo.EventObject} e
30504 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30507 * @cfg {String} title
30511 * @cfg {String} html
30515 * @cfg {String} bgimage
30519 * @cfg {String} videourl
30523 * @cfg {String} cls
30527 * @cfg {String} href
30531 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30536 * @cfg {String} (center|bottom) placetitle
30540 getAutoCreate : function()
30542 var cls = 'masonry-brick';
30544 if(this.href.length){
30545 cls += ' masonry-brick-link';
30548 if(this.bgimage.length){
30549 cls += ' masonry-brick-image';
30553 cls += ' masonry-' + this.size + '-brick';
30556 if(this.placetitle.length){
30558 switch (this.placetitle) {
30560 cls += ' masonry-center-title';
30563 cls += ' masonry-bottom-title';
30570 if(!this.html.length && !this.bgimage.length){
30571 cls += ' masonry-center-title';
30574 if(!this.html.length && this.bgimage.length){
30575 cls += ' masonry-bottom-title';
30580 cls += ' ' + this.cls;
30584 tag: (this.href.length) ? 'a' : 'div',
30589 cls: 'masonry-brick-paragraph',
30595 if(this.href.length){
30596 cfg.href = this.href;
30599 var cn = cfg.cn[0].cn;
30601 if(this.title.length){
30604 cls: 'masonry-brick-title',
30609 if(this.html.length){
30612 cls: 'masonry-brick-text',
30616 if (!this.title.length && !this.html.length) {
30617 cfg.cn[0].cls += ' hide';
30620 if(this.bgimage.length){
30623 cls: 'masonry-brick-image-view',
30627 if(this.videourl.length){
30628 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30629 // youtube support only?
30632 cls: 'masonry-brick-image-view',
30635 allowfullscreen : true
30644 initEvents: function()
30646 switch (this.size) {
30648 // this.intSize = 1;
30653 // this.intSize = 2;
30660 // this.intSize = 3;
30665 // this.intSize = 3;
30670 // this.intSize = 3;
30675 // this.intSize = 3;
30687 this.el.on('touchstart', this.onTouchStart, this);
30688 this.el.on('touchmove', this.onTouchMove, this);
30689 this.el.on('touchend', this.onTouchEnd, this);
30690 this.el.on('contextmenu', this.onContextMenu, this);
30692 this.el.on('mouseenter' ,this.enter, this);
30693 this.el.on('mouseleave', this.leave, this);
30696 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30697 this.parent().bricks.push(this);
30702 onClick: function(e, el)
30708 var time = this.endTimer - this.startTimer;
30716 e.preventDefault();
30719 enter: function(e, el)
30721 e.preventDefault();
30723 if(this.bgimage.length && this.html.length){
30724 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30728 leave: function(e, el)
30730 e.preventDefault();
30732 if(this.bgimage.length && this.html.length){
30733 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30737 onTouchStart: function(e, el)
30739 // e.preventDefault();
30741 this.touchmoved = false;
30743 if(!this.bgimage.length || !this.html.length){
30747 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30749 this.timer = new Date().getTime();
30753 onTouchMove: function(e, el)
30755 this.touchmoved = true;
30758 onContextMenu : function(e,el)
30760 e.preventDefault();
30761 e.stopPropagation();
30765 onTouchEnd: function(e, el)
30767 // e.preventDefault();
30769 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30776 if(!this.bgimage.length || !this.html.length){
30778 if(this.href.length){
30779 window.location.href = this.href;
30785 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30787 window.location.href = this.href;
30802 * @class Roo.bootstrap.Brick
30803 * @extends Roo.bootstrap.Component
30804 * Bootstrap Brick class
30807 * Create a new Brick
30808 * @param {Object} config The config object
30811 Roo.bootstrap.Brick = function(config){
30812 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30818 * When a Brick is click
30819 * @param {Roo.bootstrap.Brick} this
30820 * @param {Roo.EventObject} e
30826 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
30829 * @cfg {String} title
30833 * @cfg {String} html
30837 * @cfg {String} bgimage
30841 * @cfg {String} cls
30845 * @cfg {String} href
30849 * @cfg {String} video
30853 * @cfg {Boolean} square
30857 getAutoCreate : function()
30859 var cls = 'roo-brick';
30861 if(this.href.length){
30862 cls += ' roo-brick-link';
30865 if(this.bgimage.length){
30866 cls += ' roo-brick-image';
30869 if(!this.html.length && !this.bgimage.length){
30870 cls += ' roo-brick-center-title';
30873 if(!this.html.length && this.bgimage.length){
30874 cls += ' roo-brick-bottom-title';
30878 cls += ' ' + this.cls;
30882 tag: (this.href.length) ? 'a' : 'div',
30887 cls: 'roo-brick-paragraph',
30893 if(this.href.length){
30894 cfg.href = this.href;
30897 var cn = cfg.cn[0].cn;
30899 if(this.title.length){
30902 cls: 'roo-brick-title',
30907 if(this.html.length){
30910 cls: 'roo-brick-text',
30917 if(this.bgimage.length){
30920 cls: 'roo-brick-image-view',
30928 initEvents: function()
30930 if(this.title.length || this.html.length){
30931 this.el.on('mouseenter' ,this.enter, this);
30932 this.el.on('mouseleave', this.leave, this);
30936 Roo.EventManager.onWindowResize(this.resize, this);
30941 resize : function()
30943 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30945 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30946 // paragraph.setHeight(paragraph.getWidth());
30948 if(this.bgimage.length){
30949 var image = this.el.select('.roo-brick-image-view', true).first();
30950 image.setWidth(paragraph.getWidth());
30951 image.setHeight(paragraph.getWidth());
30956 enter: function(e, el)
30958 e.preventDefault();
30960 if(this.bgimage.length){
30961 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30962 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30966 leave: function(e, el)
30968 e.preventDefault();
30970 if(this.bgimage.length){
30971 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30972 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30982 * Ext JS Library 1.1.1
30983 * Copyright(c) 2006-2007, Ext JS, LLC.
30985 * Originally Released Under LGPL - original licence link has changed is not relivant.
30988 * <script type="text/javascript">
30993 * @class Roo.bootstrap.SplitBar
30994 * @extends Roo.util.Observable
30995 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30999 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31000 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31001 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31002 split.minSize = 100;
31003 split.maxSize = 600;
31004 split.animate = true;
31005 split.on('moved', splitterMoved);
31008 * Create a new SplitBar
31009 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
31010 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
31011 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31012 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
31013 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31014 position of the SplitBar).
31016 Roo.bootstrap.SplitBar = function(cfg){
31021 // dragElement : elm
31022 // resizingElement: el,
31024 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31025 // placement : Roo.bootstrap.SplitBar.LEFT ,
31026 // existingProxy ???
31029 this.el = Roo.get(cfg.dragElement, true);
31030 this.el.dom.unselectable = "on";
31032 this.resizingEl = Roo.get(cfg.resizingElement, true);
31036 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31037 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31040 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31043 * The minimum size of the resizing element. (Defaults to 0)
31049 * The maximum size of the resizing element. (Defaults to 2000)
31052 this.maxSize = 2000;
31055 * Whether to animate the transition to the new size
31058 this.animate = false;
31061 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31064 this.useShim = false;
31069 if(!cfg.existingProxy){
31071 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31073 this.proxy = Roo.get(cfg.existingProxy).dom;
31076 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31079 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31082 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31085 this.dragSpecs = {};
31088 * @private The adapter to use to positon and resize elements
31090 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31091 this.adapter.init(this);
31093 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31095 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31096 this.el.addClass("roo-splitbar-h");
31099 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31100 this.el.addClass("roo-splitbar-v");
31106 * Fires when the splitter is moved (alias for {@link #event-moved})
31107 * @param {Roo.bootstrap.SplitBar} this
31108 * @param {Number} newSize the new width or height
31113 * Fires when the splitter is moved
31114 * @param {Roo.bootstrap.SplitBar} this
31115 * @param {Number} newSize the new width or height
31119 * @event beforeresize
31120 * Fires before the splitter is dragged
31121 * @param {Roo.bootstrap.SplitBar} this
31123 "beforeresize" : true,
31125 "beforeapply" : true
31128 Roo.util.Observable.call(this);
31131 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31132 onStartProxyDrag : function(x, y){
31133 this.fireEvent("beforeresize", this);
31135 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
31137 o.enableDisplayMode("block");
31138 // all splitbars share the same overlay
31139 Roo.bootstrap.SplitBar.prototype.overlay = o;
31141 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31142 this.overlay.show();
31143 Roo.get(this.proxy).setDisplayed("block");
31144 var size = this.adapter.getElementSize(this);
31145 this.activeMinSize = this.getMinimumSize();;
31146 this.activeMaxSize = this.getMaximumSize();;
31147 var c1 = size - this.activeMinSize;
31148 var c2 = Math.max(this.activeMaxSize - size, 0);
31149 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31150 this.dd.resetConstraints();
31151 this.dd.setXConstraint(
31152 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
31153 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31155 this.dd.setYConstraint(0, 0);
31157 this.dd.resetConstraints();
31158 this.dd.setXConstraint(0, 0);
31159 this.dd.setYConstraint(
31160 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
31161 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31164 this.dragSpecs.startSize = size;
31165 this.dragSpecs.startPoint = [x, y];
31166 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31170 * @private Called after the drag operation by the DDProxy
31172 onEndProxyDrag : function(e){
31173 Roo.get(this.proxy).setDisplayed(false);
31174 var endPoint = Roo.lib.Event.getXY(e);
31176 this.overlay.hide();
31179 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31180 newSize = this.dragSpecs.startSize +
31181 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31182 endPoint[0] - this.dragSpecs.startPoint[0] :
31183 this.dragSpecs.startPoint[0] - endPoint[0]
31186 newSize = this.dragSpecs.startSize +
31187 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31188 endPoint[1] - this.dragSpecs.startPoint[1] :
31189 this.dragSpecs.startPoint[1] - endPoint[1]
31192 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31193 if(newSize != this.dragSpecs.startSize){
31194 if(this.fireEvent('beforeapply', this, newSize) !== false){
31195 this.adapter.setElementSize(this, newSize);
31196 this.fireEvent("moved", this, newSize);
31197 this.fireEvent("resize", this, newSize);
31203 * Get the adapter this SplitBar uses
31204 * @return The adapter object
31206 getAdapter : function(){
31207 return this.adapter;
31211 * Set the adapter this SplitBar uses
31212 * @param {Object} adapter A SplitBar adapter object
31214 setAdapter : function(adapter){
31215 this.adapter = adapter;
31216 this.adapter.init(this);
31220 * Gets the minimum size for the resizing element
31221 * @return {Number} The minimum size
31223 getMinimumSize : function(){
31224 return this.minSize;
31228 * Sets the minimum size for the resizing element
31229 * @param {Number} minSize The minimum size
31231 setMinimumSize : function(minSize){
31232 this.minSize = minSize;
31236 * Gets the maximum size for the resizing element
31237 * @return {Number} The maximum size
31239 getMaximumSize : function(){
31240 return this.maxSize;
31244 * Sets the maximum size for the resizing element
31245 * @param {Number} maxSize The maximum size
31247 setMaximumSize : function(maxSize){
31248 this.maxSize = maxSize;
31252 * Sets the initialize size for the resizing element
31253 * @param {Number} size The initial size
31255 setCurrentSize : function(size){
31256 var oldAnimate = this.animate;
31257 this.animate = false;
31258 this.adapter.setElementSize(this, size);
31259 this.animate = oldAnimate;
31263 * Destroy this splitbar.
31264 * @param {Boolean} removeEl True to remove the element
31266 destroy : function(removeEl){
31268 this.shim.remove();
31271 this.proxy.parentNode.removeChild(this.proxy);
31279 * @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.
31281 Roo.bootstrap.SplitBar.createProxy = function(dir){
31282 var proxy = new Roo.Element(document.createElement("div"));
31283 proxy.unselectable();
31284 var cls = 'roo-splitbar-proxy';
31285 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31286 document.body.appendChild(proxy.dom);
31291 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31292 * Default Adapter. It assumes the splitter and resizing element are not positioned
31293 * elements and only gets/sets the width of the element. Generally used for table based layouts.
31295 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31298 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31299 // do nothing for now
31300 init : function(s){
31304 * Called before drag operations to get the current size of the resizing element.
31305 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31307 getElementSize : function(s){
31308 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31309 return s.resizingEl.getWidth();
31311 return s.resizingEl.getHeight();
31316 * Called after drag operations to set the size of the resizing element.
31317 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31318 * @param {Number} newSize The new size to set
31319 * @param {Function} onComplete A function to be invoked when resizing is complete
31321 setElementSize : function(s, newSize, onComplete){
31322 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31324 s.resizingEl.setWidth(newSize);
31326 onComplete(s, newSize);
31329 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31334 s.resizingEl.setHeight(newSize);
31336 onComplete(s, newSize);
31339 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31346 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31347 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31348 * Adapter that moves the splitter element to align with the resized sizing element.
31349 * Used with an absolute positioned SplitBar.
31350 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31351 * document.body, make sure you assign an id to the body element.
31353 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31354 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31355 this.container = Roo.get(container);
31358 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31359 init : function(s){
31360 this.basic.init(s);
31363 getElementSize : function(s){
31364 return this.basic.getElementSize(s);
31367 setElementSize : function(s, newSize, onComplete){
31368 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31371 moveSplitter : function(s){
31372 var yes = Roo.bootstrap.SplitBar;
31373 switch(s.placement){
31375 s.el.setX(s.resizingEl.getRight());
31378 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31381 s.el.setY(s.resizingEl.getBottom());
31384 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31391 * Orientation constant - Create a vertical SplitBar
31395 Roo.bootstrap.SplitBar.VERTICAL = 1;
31398 * Orientation constant - Create a horizontal SplitBar
31402 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31405 * Placement constant - The resizing element is to the left of the splitter element
31409 Roo.bootstrap.SplitBar.LEFT = 1;
31412 * Placement constant - The resizing element is to the right of the splitter element
31416 Roo.bootstrap.SplitBar.RIGHT = 2;
31419 * Placement constant - The resizing element is positioned above the splitter element
31423 Roo.bootstrap.SplitBar.TOP = 3;
31426 * Placement constant - The resizing element is positioned under splitter element
31430 Roo.bootstrap.SplitBar.BOTTOM = 4;
31431 Roo.namespace("Roo.bootstrap.layout");/*
31433 * Ext JS Library 1.1.1
31434 * Copyright(c) 2006-2007, Ext JS, LLC.
31436 * Originally Released Under LGPL - original licence link has changed is not relivant.
31439 * <script type="text/javascript">
31443 * @class Roo.bootstrap.layout.Manager
31444 * @extends Roo.bootstrap.Component
31445 * Base class for layout managers.
31447 Roo.bootstrap.layout.Manager = function(config)
31449 Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31455 /** false to disable window resize monitoring @type Boolean */
31456 this.monitorWindowResize = true;
31461 * Fires when a layout is performed.
31462 * @param {Roo.LayoutManager} this
31466 * @event regionresized
31467 * Fires when the user resizes a region.
31468 * @param {Roo.LayoutRegion} region The resized region
31469 * @param {Number} newSize The new size (width for east/west, height for north/south)
31471 "regionresized" : true,
31473 * @event regioncollapsed
31474 * Fires when a region is collapsed.
31475 * @param {Roo.LayoutRegion} region The collapsed region
31477 "regioncollapsed" : true,
31479 * @event regionexpanded
31480 * Fires when a region is expanded.
31481 * @param {Roo.LayoutRegion} region The expanded region
31483 "regionexpanded" : true
31485 this.updating = false;
31488 this.el = Roo.get(config.el);
31494 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31499 monitorWindowResize : true,
31505 onRender : function(ct, position)
31508 this.el = Roo.get(ct);
31514 initEvents: function()
31518 // ie scrollbar fix
31519 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31520 document.body.scroll = "no";
31521 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31522 this.el.position('relative');
31524 this.id = this.el.id;
31525 this.el.addClass("roo-layout-container");
31526 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31527 if(this.el.dom != document.body ) {
31528 this.el.on('resize', this.layout,this);
31529 this.el.on('show', this.layout,this);
31535 * Returns true if this layout is currently being updated
31536 * @return {Boolean}
31538 isUpdating : function(){
31539 return this.updating;
31543 * Suspend the LayoutManager from doing auto-layouts while
31544 * making multiple add or remove calls
31546 beginUpdate : function(){
31547 this.updating = true;
31551 * Restore auto-layouts and optionally disable the manager from performing a layout
31552 * @param {Boolean} noLayout true to disable a layout update
31554 endUpdate : function(noLayout){
31555 this.updating = false;
31561 layout: function(){
31565 onRegionResized : function(region, newSize){
31566 this.fireEvent("regionresized", region, newSize);
31570 onRegionCollapsed : function(region){
31571 this.fireEvent("regioncollapsed", region);
31574 onRegionExpanded : function(region){
31575 this.fireEvent("regionexpanded", region);
31579 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31580 * performs box-model adjustments.
31581 * @return {Object} The size as an object {width: (the width), height: (the height)}
31583 getViewSize : function()
31586 if(this.el.dom != document.body){
31587 size = this.el.getSize();
31589 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31591 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31592 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31597 * Returns the Element this layout is bound to.
31598 * @return {Roo.Element}
31600 getEl : function(){
31605 * Returns the specified region.
31606 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31607 * @return {Roo.LayoutRegion}
31609 getRegion : function(target){
31610 return this.regions[target.toLowerCase()];
31613 onWindowResize : function(){
31614 if(this.monitorWindowResize){
31620 * Ext JS Library 1.1.1
31621 * Copyright(c) 2006-2007, Ext JS, LLC.
31623 * Originally Released Under LGPL - original licence link has changed is not relivant.
31626 * <script type="text/javascript">
31629 * @class Roo.bootstrap.layout.Border
31630 * @extends Roo.bootstrap.layout.Manager
31631 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31632 * please see: examples/bootstrap/nested.html<br><br>
31634 <b>The container the layout is rendered into can be either the body element or any other element.
31635 If it is not the body element, the container needs to either be an absolute positioned element,
31636 or you will need to add "position:relative" to the css of the container. You will also need to specify
31637 the container size if it is not the body element.</b>
31640 * Create a new Border
31641 * @param {Object} config Configuration options
31643 Roo.bootstrap.layout.Border = function(config){
31644 config = config || {};
31645 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31649 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31650 if(config[region]){
31651 config[region].region = region;
31652 this.addRegion(config[region]);
31658 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
31660 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31662 * Creates and adds a new region if it doesn't already exist.
31663 * @param {String} target The target region key (north, south, east, west or center).
31664 * @param {Object} config The regions config object
31665 * @return {BorderLayoutRegion} The new region
31667 addRegion : function(config)
31669 if(!this.regions[config.region]){
31670 var r = this.factory(config);
31671 this.bindRegion(r);
31673 return this.regions[config.region];
31677 bindRegion : function(r){
31678 this.regions[r.config.region] = r;
31680 r.on("visibilitychange", this.layout, this);
31681 r.on("paneladded", this.layout, this);
31682 r.on("panelremoved", this.layout, this);
31683 r.on("invalidated", this.layout, this);
31684 r.on("resized", this.onRegionResized, this);
31685 r.on("collapsed", this.onRegionCollapsed, this);
31686 r.on("expanded", this.onRegionExpanded, this);
31690 * Performs a layout update.
31692 layout : function()
31694 if(this.updating) {
31697 var size = this.getViewSize();
31698 var w = size.width;
31699 var h = size.height;
31704 //var x = 0, y = 0;
31706 var rs = this.regions;
31707 var north = rs["north"];
31708 var south = rs["south"];
31709 var west = rs["west"];
31710 var east = rs["east"];
31711 var center = rs["center"];
31712 //if(this.hideOnLayout){ // not supported anymore
31713 //c.el.setStyle("display", "none");
31715 if(north && north.isVisible()){
31716 var b = north.getBox();
31717 var m = north.getMargins();
31718 b.width = w - (m.left+m.right);
31721 centerY = b.height + b.y + m.bottom;
31722 centerH -= centerY;
31723 north.updateBox(this.safeBox(b));
31725 if(south && south.isVisible()){
31726 var b = south.getBox();
31727 var m = south.getMargins();
31728 b.width = w - (m.left+m.right);
31730 var totalHeight = (b.height + m.top + m.bottom);
31731 b.y = h - totalHeight + m.top;
31732 centerH -= totalHeight;
31733 south.updateBox(this.safeBox(b));
31735 if(west && west.isVisible()){
31736 var b = west.getBox();
31737 var m = west.getMargins();
31738 b.height = centerH - (m.top+m.bottom);
31740 b.y = centerY + m.top;
31741 var totalWidth = (b.width + m.left + m.right);
31742 centerX += totalWidth;
31743 centerW -= totalWidth;
31744 west.updateBox(this.safeBox(b));
31746 if(east && east.isVisible()){
31747 var b = east.getBox();
31748 var m = east.getMargins();
31749 b.height = centerH - (m.top+m.bottom);
31750 var totalWidth = (b.width + m.left + m.right);
31751 b.x = w - totalWidth + m.left;
31752 b.y = centerY + m.top;
31753 centerW -= totalWidth;
31754 east.updateBox(this.safeBox(b));
31757 var m = center.getMargins();
31759 x: centerX + m.left,
31760 y: centerY + m.top,
31761 width: centerW - (m.left+m.right),
31762 height: centerH - (m.top+m.bottom)
31764 //if(this.hideOnLayout){
31765 //center.el.setStyle("display", "block");
31767 center.updateBox(this.safeBox(centerBox));
31770 this.fireEvent("layout", this);
31774 safeBox : function(box){
31775 box.width = Math.max(0, box.width);
31776 box.height = Math.max(0, box.height);
31781 * Adds a ContentPanel (or subclass) to this layout.
31782 * @param {String} target The target region key (north, south, east, west or center).
31783 * @param {Roo.ContentPanel} panel The panel to add
31784 * @return {Roo.ContentPanel} The added panel
31786 add : function(target, panel){
31788 target = target.toLowerCase();
31789 return this.regions[target].add(panel);
31793 * Remove a ContentPanel (or subclass) to this layout.
31794 * @param {String} target The target region key (north, south, east, west or center).
31795 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31796 * @return {Roo.ContentPanel} The removed panel
31798 remove : function(target, panel){
31799 target = target.toLowerCase();
31800 return this.regions[target].remove(panel);
31804 * Searches all regions for a panel with the specified id
31805 * @param {String} panelId
31806 * @return {Roo.ContentPanel} The panel or null if it wasn't found
31808 findPanel : function(panelId){
31809 var rs = this.regions;
31810 for(var target in rs){
31811 if(typeof rs[target] != "function"){
31812 var p = rs[target].getPanel(panelId);
31822 * Searches all regions for a panel with the specified id and activates (shows) it.
31823 * @param {String/ContentPanel} panelId The panels id or the panel itself
31824 * @return {Roo.ContentPanel} The shown panel or null
31826 showPanel : function(panelId) {
31827 var rs = this.regions;
31828 for(var target in rs){
31829 var r = rs[target];
31830 if(typeof r != "function"){
31831 if(r.hasPanel(panelId)){
31832 return r.showPanel(panelId);
31840 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31841 * @param {Roo.state.Provider} provider (optional) An alternate state provider
31844 restoreState : function(provider){
31846 provider = Roo.state.Manager;
31848 var sm = new Roo.LayoutStateManager();
31849 sm.init(this, provider);
31855 * Adds a xtype elements to the layout.
31859 xtype : 'ContentPanel',
31866 xtype : 'NestedLayoutPanel',
31872 items : [ ... list of content panels or nested layout panels.. ]
31876 * @param {Object} cfg Xtype definition of item to add.
31878 addxtype : function(cfg)
31880 // basically accepts a pannel...
31881 // can accept a layout region..!?!?
31882 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31885 // theory? children can only be panels??
31887 //if (!cfg.xtype.match(/Panel$/)) {
31892 if (typeof(cfg.region) == 'undefined') {
31893 Roo.log("Failed to add Panel, region was not set");
31897 var region = cfg.region;
31903 xitems = cfg.items;
31910 case 'Content': // ContentPanel (el, cfg)
31911 case 'Scroll': // ContentPanel (el, cfg)
31913 cfg.autoCreate = true;
31914 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31916 // var el = this.el.createChild();
31917 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31920 this.add(region, ret);
31924 case 'TreePanel': // our new panel!
31925 cfg.el = this.el.createChild();
31926 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31927 this.add(region, ret);
31932 // create a new Layout (which is a Border Layout...
31934 var clayout = cfg.layout;
31935 clayout.el = this.el.createChild();
31936 clayout.items = clayout.items || [];
31940 // replace this exitems with the clayout ones..
31941 xitems = clayout.items;
31943 // force background off if it's in center...
31944 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31945 cfg.background = false;
31947 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
31950 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31951 //console.log('adding nested layout panel ' + cfg.toSource());
31952 this.add(region, ret);
31953 nb = {}; /// find first...
31958 // needs grid and region
31960 //var el = this.getRegion(region).el.createChild();
31962 *var el = this.el.createChild();
31963 // create the grid first...
31964 cfg.grid.container = el;
31965 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31968 if (region == 'center' && this.active ) {
31969 cfg.background = false;
31972 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31974 this.add(region, ret);
31976 if (cfg.background) {
31977 // render grid on panel activation (if panel background)
31978 ret.on('activate', function(gp) {
31979 if (!gp.grid.rendered) {
31980 // gp.grid.render(el);
31984 // cfg.grid.render(el);
31990 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31991 // it was the old xcomponent building that caused this before.
31992 // espeically if border is the top element in the tree.
32002 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32004 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32005 this.add(region, ret);
32009 throw "Can not add '" + cfg.xtype + "' to Border";
32015 this.beginUpdate();
32019 Roo.each(xitems, function(i) {
32020 region = nb && i.region ? i.region : false;
32022 var add = ret.addxtype(i);
32025 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32026 if (!i.background) {
32027 abn[region] = nb[region] ;
32034 // make the last non-background panel active..
32035 //if (nb) { Roo.log(abn); }
32038 for(var r in abn) {
32039 region = this.getRegion(r);
32041 // tried using nb[r], but it does not work..
32043 region.showPanel(abn[r]);
32054 factory : function(cfg)
32057 var validRegions = Roo.bootstrap.layout.Border.regions;
32059 var target = cfg.region;
32062 var r = Roo.bootstrap.layout;
32066 return new r.North(cfg);
32068 return new r.South(cfg);
32070 return new r.East(cfg);
32072 return new r.West(cfg);
32074 return new r.Center(cfg);
32076 throw 'Layout region "'+target+'" not supported.';
32083 * Ext JS Library 1.1.1
32084 * Copyright(c) 2006-2007, Ext JS, LLC.
32086 * Originally Released Under LGPL - original licence link has changed is not relivant.
32089 * <script type="text/javascript">
32093 * @class Roo.bootstrap.layout.Basic
32094 * @extends Roo.util.Observable
32095 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32096 * and does not have a titlebar, tabs or any other features. All it does is size and position
32097 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32098 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32099 * @cfg {string} region the region that it inhabits..
32100 * @cfg {bool} skipConfig skip config?
32104 Roo.bootstrap.layout.Basic = function(config){
32106 this.mgr = config.mgr;
32108 this.position = config.region;
32110 var skipConfig = config.skipConfig;
32114 * @scope Roo.BasicLayoutRegion
32118 * @event beforeremove
32119 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32120 * @param {Roo.LayoutRegion} this
32121 * @param {Roo.ContentPanel} panel The panel
32122 * @param {Object} e The cancel event object
32124 "beforeremove" : true,
32126 * @event invalidated
32127 * Fires when the layout for this region is changed.
32128 * @param {Roo.LayoutRegion} this
32130 "invalidated" : true,
32132 * @event visibilitychange
32133 * Fires when this region is shown or hidden
32134 * @param {Roo.LayoutRegion} this
32135 * @param {Boolean} visibility true or false
32137 "visibilitychange" : true,
32139 * @event paneladded
32140 * Fires when a panel is added.
32141 * @param {Roo.LayoutRegion} this
32142 * @param {Roo.ContentPanel} panel The panel
32144 "paneladded" : true,
32146 * @event panelremoved
32147 * Fires when a panel is removed.
32148 * @param {Roo.LayoutRegion} this
32149 * @param {Roo.ContentPanel} panel The panel
32151 "panelremoved" : true,
32153 * @event beforecollapse
32154 * Fires when this region before collapse.
32155 * @param {Roo.LayoutRegion} this
32157 "beforecollapse" : true,
32160 * Fires when this region is collapsed.
32161 * @param {Roo.LayoutRegion} this
32163 "collapsed" : true,
32166 * Fires when this region is expanded.
32167 * @param {Roo.LayoutRegion} this
32172 * Fires when this region is slid into view.
32173 * @param {Roo.LayoutRegion} this
32175 "slideshow" : true,
32178 * Fires when this region slides out of view.
32179 * @param {Roo.LayoutRegion} this
32181 "slidehide" : true,
32183 * @event panelactivated
32184 * Fires when a panel is activated.
32185 * @param {Roo.LayoutRegion} this
32186 * @param {Roo.ContentPanel} panel The activated panel
32188 "panelactivated" : true,
32191 * Fires when the user resizes this region.
32192 * @param {Roo.LayoutRegion} this
32193 * @param {Number} newSize The new size (width for east/west, height for north/south)
32197 /** A collection of panels in this region. @type Roo.util.MixedCollection */
32198 this.panels = new Roo.util.MixedCollection();
32199 this.panels.getKey = this.getPanelId.createDelegate(this);
32201 this.activePanel = null;
32202 // ensure listeners are added...
32204 if (config.listeners || config.events) {
32205 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32206 listeners : config.listeners || {},
32207 events : config.events || {}
32211 if(skipConfig !== true){
32212 this.applyConfig(config);
32216 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32218 getPanelId : function(p){
32222 applyConfig : function(config){
32223 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32224 this.config = config;
32229 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
32230 * the width, for horizontal (north, south) the height.
32231 * @param {Number} newSize The new width or height
32233 resizeTo : function(newSize){
32234 var el = this.el ? this.el :
32235 (this.activePanel ? this.activePanel.getEl() : null);
32237 switch(this.position){
32240 el.setWidth(newSize);
32241 this.fireEvent("resized", this, newSize);
32245 el.setHeight(newSize);
32246 this.fireEvent("resized", this, newSize);
32252 getBox : function(){
32253 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32256 getMargins : function(){
32257 return this.margins;
32260 updateBox : function(box){
32262 var el = this.activePanel.getEl();
32263 el.dom.style.left = box.x + "px";
32264 el.dom.style.top = box.y + "px";
32265 this.activePanel.setSize(box.width, box.height);
32269 * Returns the container element for this region.
32270 * @return {Roo.Element}
32272 getEl : function(){
32273 return this.activePanel;
32277 * Returns true if this region is currently visible.
32278 * @return {Boolean}
32280 isVisible : function(){
32281 return this.activePanel ? true : false;
32284 setActivePanel : function(panel){
32285 panel = this.getPanel(panel);
32286 if(this.activePanel && this.activePanel != panel){
32287 this.activePanel.setActiveState(false);
32288 this.activePanel.getEl().setLeftTop(-10000,-10000);
32290 this.activePanel = panel;
32291 panel.setActiveState(true);
32293 panel.setSize(this.box.width, this.box.height);
32295 this.fireEvent("panelactivated", this, panel);
32296 this.fireEvent("invalidated");
32300 * Show the specified panel.
32301 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32302 * @return {Roo.ContentPanel} The shown panel or null
32304 showPanel : function(panel){
32305 panel = this.getPanel(panel);
32307 this.setActivePanel(panel);
32313 * Get the active panel for this region.
32314 * @return {Roo.ContentPanel} The active panel or null
32316 getActivePanel : function(){
32317 return this.activePanel;
32321 * Add the passed ContentPanel(s)
32322 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32323 * @return {Roo.ContentPanel} The panel added (if only one was added)
32325 add : function(panel){
32326 if(arguments.length > 1){
32327 for(var i = 0, len = arguments.length; i < len; i++) {
32328 this.add(arguments[i]);
32332 if(this.hasPanel(panel)){
32333 this.showPanel(panel);
32336 var el = panel.getEl();
32337 if(el.dom.parentNode != this.mgr.el.dom){
32338 this.mgr.el.dom.appendChild(el.dom);
32340 if(panel.setRegion){
32341 panel.setRegion(this);
32343 this.panels.add(panel);
32344 el.setStyle("position", "absolute");
32345 if(!panel.background){
32346 this.setActivePanel(panel);
32347 if(this.config.initialSize && this.panels.getCount()==1){
32348 this.resizeTo(this.config.initialSize);
32351 this.fireEvent("paneladded", this, panel);
32356 * Returns true if the panel is in this region.
32357 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32358 * @return {Boolean}
32360 hasPanel : function(panel){
32361 if(typeof panel == "object"){ // must be panel obj
32362 panel = panel.getId();
32364 return this.getPanel(panel) ? true : false;
32368 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32369 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32370 * @param {Boolean} preservePanel Overrides the config preservePanel option
32371 * @return {Roo.ContentPanel} The panel that was removed
32373 remove : function(panel, preservePanel){
32374 panel = this.getPanel(panel);
32379 this.fireEvent("beforeremove", this, panel, e);
32380 if(e.cancel === true){
32383 var panelId = panel.getId();
32384 this.panels.removeKey(panelId);
32389 * Returns the panel specified or null if it's not in this region.
32390 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32391 * @return {Roo.ContentPanel}
32393 getPanel : function(id){
32394 if(typeof id == "object"){ // must be panel obj
32397 return this.panels.get(id);
32401 * Returns this regions position (north/south/east/west/center).
32404 getPosition: function(){
32405 return this.position;
32409 * Ext JS Library 1.1.1
32410 * Copyright(c) 2006-2007, Ext JS, LLC.
32412 * Originally Released Under LGPL - original licence link has changed is not relivant.
32415 * <script type="text/javascript">
32419 * @class Roo.bootstrap.layout.Region
32420 * @extends Roo.bootstrap.layout.Basic
32421 * This class represents a region in a layout manager.
32423 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32424 * @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})
32425 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
32426 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
32427 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
32428 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
32429 * @cfg {String} title The title for the region (overrides panel titles)
32430 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
32431 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32432 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
32433 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32434 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
32435 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32436 * the space available, similar to FireFox 1.5 tabs (defaults to false)
32437 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
32438 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
32439 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
32441 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
32442 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
32443 * @cfg {Boolean} disableTabTips True to disable tab tooltips
32444 * @cfg {Number} width For East/West panels
32445 * @cfg {Number} height For North/South panels
32446 * @cfg {Boolean} split To show the splitter
32447 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
32449 * @cfg {string} cls Extra CSS classes to add to region
32451 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32452 * @cfg {string} region the region that it inhabits..
32455 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
32456 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
32458 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
32459 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
32460 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
32462 Roo.bootstrap.layout.Region = function(config)
32464 this.applyConfig(config);
32466 var mgr = config.mgr;
32467 var pos = config.region;
32468 config.skipConfig = true;
32469 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32472 this.onRender(mgr.el);
32475 this.visible = true;
32476 this.collapsed = false;
32479 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32481 position: '', // set by wrapper (eg. north/south etc..)
32483 createBody : function(){
32484 /** This region's body element
32485 * @type Roo.Element */
32486 this.bodyEl = this.el.createChild({
32488 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32492 onRender: function(ctr, pos)
32494 var dh = Roo.DomHelper;
32495 /** This region's container element
32496 * @type Roo.Element */
32497 this.el = dh.append(ctr.dom, {
32499 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32501 /** This region's title element
32502 * @type Roo.Element */
32504 this.titleEl = dh.append(this.el.dom,
32507 unselectable: "on",
32508 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32510 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
32511 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32514 this.titleEl.enableDisplayMode();
32515 /** This region's title text element
32516 * @type HTMLElement */
32517 this.titleTextEl = this.titleEl.dom.firstChild;
32518 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32520 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32521 this.closeBtn.enableDisplayMode();
32522 this.closeBtn.on("click", this.closeClicked, this);
32523 this.closeBtn.hide();
32525 this.createBody(this.config);
32526 if(this.config.hideWhenEmpty){
32528 this.on("paneladded", this.validateVisibility, this);
32529 this.on("panelremoved", this.validateVisibility, this);
32531 if(this.autoScroll){
32532 this.bodyEl.setStyle("overflow", "auto");
32534 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32536 //if(c.titlebar !== false){
32537 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32538 this.titleEl.hide();
32540 this.titleEl.show();
32541 if(this.config.title){
32542 this.titleTextEl.innerHTML = this.config.title;
32546 if(this.config.collapsed){
32547 this.collapse(true);
32549 if(this.config.hidden){
32554 applyConfig : function(c)
32557 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32558 var dh = Roo.DomHelper;
32559 if(c.titlebar !== false){
32560 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32561 this.collapseBtn.on("click", this.collapse, this);
32562 this.collapseBtn.enableDisplayMode();
32564 if(c.showPin === true || this.showPin){
32565 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32566 this.stickBtn.enableDisplayMode();
32567 this.stickBtn.on("click", this.expand, this);
32568 this.stickBtn.hide();
32573 /** This region's collapsed element
32574 * @type Roo.Element */
32577 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32578 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32581 if(c.floatable !== false){
32582 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32583 this.collapsedEl.on("click", this.collapseClick, this);
32586 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32587 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32588 id: "message", unselectable: "on", style:{"float":"left"}});
32589 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32591 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32592 this.expandBtn.on("click", this.expand, this);
32596 if(this.collapseBtn){
32597 this.collapseBtn.setVisible(c.collapsible == true);
32600 this.cmargins = c.cmargins || this.cmargins ||
32601 (this.position == "west" || this.position == "east" ?
32602 {top: 0, left: 2, right:2, bottom: 0} :
32603 {top: 2, left: 0, right:0, bottom: 2});
32605 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32608 this.bottomTabs = c.tabPosition != "top";
32610 this.autoScroll = c.autoScroll || false;
32615 this.duration = c.duration || .30;
32616 this.slideDuration = c.slideDuration || .45;
32621 * Returns true if this region is currently visible.
32622 * @return {Boolean}
32624 isVisible : function(){
32625 return this.visible;
32629 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32630 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
32632 //setCollapsedTitle : function(title){
32633 // title = title || " ";
32634 // if(this.collapsedTitleTextEl){
32635 // this.collapsedTitleTextEl.innerHTML = title;
32639 getBox : function(){
32641 // if(!this.collapsed){
32642 b = this.el.getBox(false, true);
32644 // b = this.collapsedEl.getBox(false, true);
32649 getMargins : function(){
32650 return this.margins;
32651 //return this.collapsed ? this.cmargins : this.margins;
32654 highlight : function(){
32655 this.el.addClass("x-layout-panel-dragover");
32658 unhighlight : function(){
32659 this.el.removeClass("x-layout-panel-dragover");
32662 updateBox : function(box)
32665 if(!this.collapsed){
32666 this.el.dom.style.left = box.x + "px";
32667 this.el.dom.style.top = box.y + "px";
32668 this.updateBody(box.width, box.height);
32670 this.collapsedEl.dom.style.left = box.x + "px";
32671 this.collapsedEl.dom.style.top = box.y + "px";
32672 this.collapsedEl.setSize(box.width, box.height);
32675 this.tabs.autoSizeTabs();
32679 updateBody : function(w, h)
32682 this.el.setWidth(w);
32683 w -= this.el.getBorderWidth("rl");
32684 if(this.config.adjustments){
32685 w += this.config.adjustments[0];
32689 this.el.setHeight(h);
32690 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32691 h -= this.el.getBorderWidth("tb");
32692 if(this.config.adjustments){
32693 h += this.config.adjustments[1];
32695 this.bodyEl.setHeight(h);
32697 h = this.tabs.syncHeight(h);
32700 if(this.panelSize){
32701 w = w !== null ? w : this.panelSize.width;
32702 h = h !== null ? h : this.panelSize.height;
32704 if(this.activePanel){
32705 var el = this.activePanel.getEl();
32706 w = w !== null ? w : el.getWidth();
32707 h = h !== null ? h : el.getHeight();
32708 this.panelSize = {width: w, height: h};
32709 this.activePanel.setSize(w, h);
32711 if(Roo.isIE && this.tabs){
32712 this.tabs.el.repaint();
32717 * Returns the container element for this region.
32718 * @return {Roo.Element}
32720 getEl : function(){
32725 * Hides this region.
32728 //if(!this.collapsed){
32729 this.el.dom.style.left = "-2000px";
32732 // this.collapsedEl.dom.style.left = "-2000px";
32733 // this.collapsedEl.hide();
32735 this.visible = false;
32736 this.fireEvent("visibilitychange", this, false);
32740 * Shows this region if it was previously hidden.
32743 //if(!this.collapsed){
32746 // this.collapsedEl.show();
32748 this.visible = true;
32749 this.fireEvent("visibilitychange", this, true);
32752 closeClicked : function(){
32753 if(this.activePanel){
32754 this.remove(this.activePanel);
32758 collapseClick : function(e){
32760 e.stopPropagation();
32763 e.stopPropagation();
32769 * Collapses this region.
32770 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32773 collapse : function(skipAnim, skipCheck = false){
32774 if(this.collapsed) {
32778 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32780 this.collapsed = true;
32782 this.split.el.hide();
32784 if(this.config.animate && skipAnim !== true){
32785 this.fireEvent("invalidated", this);
32786 this.animateCollapse();
32788 this.el.setLocation(-20000,-20000);
32790 this.collapsedEl.show();
32791 this.fireEvent("collapsed", this);
32792 this.fireEvent("invalidated", this);
32798 animateCollapse : function(){
32803 * Expands this region if it was previously collapsed.
32804 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32805 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32808 expand : function(e, skipAnim){
32810 e.stopPropagation();
32812 if(!this.collapsed || this.el.hasActiveFx()) {
32816 this.afterSlideIn();
32819 this.collapsed = false;
32820 if(this.config.animate && skipAnim !== true){
32821 this.animateExpand();
32825 this.split.el.show();
32827 this.collapsedEl.setLocation(-2000,-2000);
32828 this.collapsedEl.hide();
32829 this.fireEvent("invalidated", this);
32830 this.fireEvent("expanded", this);
32834 animateExpand : function(){
32838 initTabs : function()
32840 this.bodyEl.setStyle("overflow", "hidden");
32841 var ts = new Roo.bootstrap.panel.Tabs({
32842 el: this.bodyEl.dom,
32843 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32844 disableTooltips: this.config.disableTabTips,
32845 toolbar : this.config.toolbar
32848 if(this.config.hideTabs){
32849 ts.stripWrap.setDisplayed(false);
32852 ts.resizeTabs = this.config.resizeTabs === true;
32853 ts.minTabWidth = this.config.minTabWidth || 40;
32854 ts.maxTabWidth = this.config.maxTabWidth || 250;
32855 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32856 ts.monitorResize = false;
32857 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32858 ts.bodyEl.addClass('roo-layout-tabs-body');
32859 this.panels.each(this.initPanelAsTab, this);
32862 initPanelAsTab : function(panel){
32863 var ti = this.tabs.addTab(
32865 panel.getTitle(), null,
32866 this.config.closeOnTab && panel.isClosable()
32868 if(panel.tabTip !== undefined){
32869 ti.setTooltip(panel.tabTip);
32871 ti.on("activate", function(){
32872 this.setActivePanel(panel);
32875 if(this.config.closeOnTab){
32876 ti.on("beforeclose", function(t, e){
32878 this.remove(panel);
32884 updatePanelTitle : function(panel, title)
32886 if(this.activePanel == panel){
32887 this.updateTitle(title);
32890 var ti = this.tabs.getTab(panel.getEl().id);
32892 if(panel.tabTip !== undefined){
32893 ti.setTooltip(panel.tabTip);
32898 updateTitle : function(title){
32899 if(this.titleTextEl && !this.config.title){
32900 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
32904 setActivePanel : function(panel)
32906 panel = this.getPanel(panel);
32907 if(this.activePanel && this.activePanel != panel){
32908 this.activePanel.setActiveState(false);
32910 this.activePanel = panel;
32911 panel.setActiveState(true);
32912 if(this.panelSize){
32913 panel.setSize(this.panelSize.width, this.panelSize.height);
32916 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32918 this.updateTitle(panel.getTitle());
32920 this.fireEvent("invalidated", this);
32922 this.fireEvent("panelactivated", this, panel);
32926 * Shows the specified panel.
32927 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32928 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32930 showPanel : function(panel)
32932 panel = this.getPanel(panel);
32935 var tab = this.tabs.getTab(panel.getEl().id);
32936 if(tab.isHidden()){
32937 this.tabs.unhideTab(tab.id);
32941 this.setActivePanel(panel);
32948 * Get the active panel for this region.
32949 * @return {Roo.ContentPanel} The active panel or null
32951 getActivePanel : function(){
32952 return this.activePanel;
32955 validateVisibility : function(){
32956 if(this.panels.getCount() < 1){
32957 this.updateTitle(" ");
32958 this.closeBtn.hide();
32961 if(!this.isVisible()){
32968 * Adds the passed ContentPanel(s) to this region.
32969 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32970 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32972 add : function(panel){
32973 if(arguments.length > 1){
32974 for(var i = 0, len = arguments.length; i < len; i++) {
32975 this.add(arguments[i]);
32979 if(this.hasPanel(panel)){
32980 this.showPanel(panel);
32983 panel.setRegion(this);
32984 this.panels.add(panel);
32985 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32986 this.bodyEl.dom.appendChild(panel.getEl().dom);
32987 if(panel.background !== true){
32988 this.setActivePanel(panel);
32990 this.fireEvent("paneladded", this, panel);
32996 this.initPanelAsTab(panel);
33000 if(panel.background !== true){
33001 this.tabs.activate(panel.getEl().id);
33003 this.fireEvent("paneladded", this, panel);
33008 * Hides the tab for the specified panel.
33009 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33011 hidePanel : function(panel){
33012 if(this.tabs && (panel = this.getPanel(panel))){
33013 this.tabs.hideTab(panel.getEl().id);
33018 * Unhides the tab for a previously hidden panel.
33019 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33021 unhidePanel : function(panel){
33022 if(this.tabs && (panel = this.getPanel(panel))){
33023 this.tabs.unhideTab(panel.getEl().id);
33027 clearPanels : function(){
33028 while(this.panels.getCount() > 0){
33029 this.remove(this.panels.first());
33034 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33035 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33036 * @param {Boolean} preservePanel Overrides the config preservePanel option
33037 * @return {Roo.ContentPanel} The panel that was removed
33039 remove : function(panel, preservePanel)
33041 panel = this.getPanel(panel);
33046 this.fireEvent("beforeremove", this, panel, e);
33047 if(e.cancel === true){
33050 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33051 var panelId = panel.getId();
33052 this.panels.removeKey(panelId);
33054 document.body.appendChild(panel.getEl().dom);
33057 this.tabs.removeTab(panel.getEl().id);
33058 }else if (!preservePanel){
33059 this.bodyEl.dom.removeChild(panel.getEl().dom);
33061 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33062 var p = this.panels.first();
33063 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33064 tempEl.appendChild(p.getEl().dom);
33065 this.bodyEl.update("");
33066 this.bodyEl.dom.appendChild(p.getEl().dom);
33068 this.updateTitle(p.getTitle());
33070 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33071 this.setActivePanel(p);
33073 panel.setRegion(null);
33074 if(this.activePanel == panel){
33075 this.activePanel = null;
33077 if(this.config.autoDestroy !== false && preservePanel !== true){
33078 try{panel.destroy();}catch(e){}
33080 this.fireEvent("panelremoved", this, panel);
33085 * Returns the TabPanel component used by this region
33086 * @return {Roo.TabPanel}
33088 getTabs : function(){
33092 createTool : function(parentEl, className){
33093 var btn = Roo.DomHelper.append(parentEl, {
33095 cls: "x-layout-tools-button",
33098 cls: "roo-layout-tools-button-inner " + className,
33102 btn.addClassOnOver("roo-layout-tools-button-over");
33107 * Ext JS Library 1.1.1
33108 * Copyright(c) 2006-2007, Ext JS, LLC.
33110 * Originally Released Under LGPL - original licence link has changed is not relivant.
33113 * <script type="text/javascript">
33119 * @class Roo.SplitLayoutRegion
33120 * @extends Roo.LayoutRegion
33121 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33123 Roo.bootstrap.layout.Split = function(config){
33124 this.cursor = config.cursor;
33125 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33128 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33130 splitTip : "Drag to resize.",
33131 collapsibleSplitTip : "Drag to resize. Double click to hide.",
33132 useSplitTips : false,
33134 applyConfig : function(config){
33135 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33138 onRender : function(ctr,pos) {
33140 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33141 if(!this.config.split){
33146 var splitEl = Roo.DomHelper.append(ctr.dom, {
33148 id: this.el.id + "-split",
33149 cls: "roo-layout-split roo-layout-split-"+this.position,
33152 /** The SplitBar for this region
33153 * @type Roo.SplitBar */
33154 // does not exist yet...
33155 Roo.log([this.position, this.orientation]);
33157 this.split = new Roo.bootstrap.SplitBar({
33158 dragElement : splitEl,
33159 resizingElement: this.el,
33160 orientation : this.orientation
33163 this.split.on("moved", this.onSplitMove, this);
33164 this.split.useShim = this.config.useShim === true;
33165 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33166 if(this.useSplitTips){
33167 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33169 //if(config.collapsible){
33170 // this.split.el.on("dblclick", this.collapse, this);
33173 if(typeof this.config.minSize != "undefined"){
33174 this.split.minSize = this.config.minSize;
33176 if(typeof this.config.maxSize != "undefined"){
33177 this.split.maxSize = this.config.maxSize;
33179 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33180 this.hideSplitter();
33185 getHMaxSize : function(){
33186 var cmax = this.config.maxSize || 10000;
33187 var center = this.mgr.getRegion("center");
33188 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33191 getVMaxSize : function(){
33192 var cmax = this.config.maxSize || 10000;
33193 var center = this.mgr.getRegion("center");
33194 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33197 onSplitMove : function(split, newSize){
33198 this.fireEvent("resized", this, newSize);
33202 * Returns the {@link Roo.SplitBar} for this region.
33203 * @return {Roo.SplitBar}
33205 getSplitBar : function(){
33210 this.hideSplitter();
33211 Roo.bootstrap.layout.Split.superclass.hide.call(this);
33214 hideSplitter : function(){
33216 this.split.el.setLocation(-2000,-2000);
33217 this.split.el.hide();
33223 this.split.el.show();
33225 Roo.bootstrap.layout.Split.superclass.show.call(this);
33228 beforeSlide: function(){
33229 if(Roo.isGecko){// firefox overflow auto bug workaround
33230 this.bodyEl.clip();
33232 this.tabs.bodyEl.clip();
33234 if(this.activePanel){
33235 this.activePanel.getEl().clip();
33237 if(this.activePanel.beforeSlide){
33238 this.activePanel.beforeSlide();
33244 afterSlide : function(){
33245 if(Roo.isGecko){// firefox overflow auto bug workaround
33246 this.bodyEl.unclip();
33248 this.tabs.bodyEl.unclip();
33250 if(this.activePanel){
33251 this.activePanel.getEl().unclip();
33252 if(this.activePanel.afterSlide){
33253 this.activePanel.afterSlide();
33259 initAutoHide : function(){
33260 if(this.autoHide !== false){
33261 if(!this.autoHideHd){
33262 var st = new Roo.util.DelayedTask(this.slideIn, this);
33263 this.autoHideHd = {
33264 "mouseout": function(e){
33265 if(!e.within(this.el, true)){
33269 "mouseover" : function(e){
33275 this.el.on(this.autoHideHd);
33279 clearAutoHide : function(){
33280 if(this.autoHide !== false){
33281 this.el.un("mouseout", this.autoHideHd.mouseout);
33282 this.el.un("mouseover", this.autoHideHd.mouseover);
33286 clearMonitor : function(){
33287 Roo.get(document).un("click", this.slideInIf, this);
33290 // these names are backwards but not changed for compat
33291 slideOut : function(){
33292 if(this.isSlid || this.el.hasActiveFx()){
33295 this.isSlid = true;
33296 if(this.collapseBtn){
33297 this.collapseBtn.hide();
33299 this.closeBtnState = this.closeBtn.getStyle('display');
33300 this.closeBtn.hide();
33302 this.stickBtn.show();
33305 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33306 this.beforeSlide();
33307 this.el.setStyle("z-index", 10001);
33308 this.el.slideIn(this.getSlideAnchor(), {
33309 callback: function(){
33311 this.initAutoHide();
33312 Roo.get(document).on("click", this.slideInIf, this);
33313 this.fireEvent("slideshow", this);
33320 afterSlideIn : function(){
33321 this.clearAutoHide();
33322 this.isSlid = false;
33323 this.clearMonitor();
33324 this.el.setStyle("z-index", "");
33325 if(this.collapseBtn){
33326 this.collapseBtn.show();
33328 this.closeBtn.setStyle('display', this.closeBtnState);
33330 this.stickBtn.hide();
33332 this.fireEvent("slidehide", this);
33335 slideIn : function(cb){
33336 if(!this.isSlid || this.el.hasActiveFx()){
33340 this.isSlid = false;
33341 this.beforeSlide();
33342 this.el.slideOut(this.getSlideAnchor(), {
33343 callback: function(){
33344 this.el.setLeftTop(-10000, -10000);
33346 this.afterSlideIn();
33354 slideInIf : function(e){
33355 if(!e.within(this.el)){
33360 animateCollapse : function(){
33361 this.beforeSlide();
33362 this.el.setStyle("z-index", 20000);
33363 var anchor = this.getSlideAnchor();
33364 this.el.slideOut(anchor, {
33365 callback : function(){
33366 this.el.setStyle("z-index", "");
33367 this.collapsedEl.slideIn(anchor, {duration:.3});
33369 this.el.setLocation(-10000,-10000);
33371 this.fireEvent("collapsed", this);
33378 animateExpand : function(){
33379 this.beforeSlide();
33380 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33381 this.el.setStyle("z-index", 20000);
33382 this.collapsedEl.hide({
33385 this.el.slideIn(this.getSlideAnchor(), {
33386 callback : function(){
33387 this.el.setStyle("z-index", "");
33390 this.split.el.show();
33392 this.fireEvent("invalidated", this);
33393 this.fireEvent("expanded", this);
33421 getAnchor : function(){
33422 return this.anchors[this.position];
33425 getCollapseAnchor : function(){
33426 return this.canchors[this.position];
33429 getSlideAnchor : function(){
33430 return this.sanchors[this.position];
33433 getAlignAdj : function(){
33434 var cm = this.cmargins;
33435 switch(this.position){
33451 getExpandAdj : function(){
33452 var c = this.collapsedEl, cm = this.cmargins;
33453 switch(this.position){
33455 return [-(cm.right+c.getWidth()+cm.left), 0];
33458 return [cm.right+c.getWidth()+cm.left, 0];
33461 return [0, -(cm.top+cm.bottom+c.getHeight())];
33464 return [0, cm.top+cm.bottom+c.getHeight()];
33470 * Ext JS Library 1.1.1
33471 * Copyright(c) 2006-2007, Ext JS, LLC.
33473 * Originally Released Under LGPL - original licence link has changed is not relivant.
33476 * <script type="text/javascript">
33479 * These classes are private internal classes
33481 Roo.bootstrap.layout.Center = function(config){
33482 config.region = "center";
33483 Roo.bootstrap.layout.Region.call(this, config);
33484 this.visible = true;
33485 this.minWidth = config.minWidth || 20;
33486 this.minHeight = config.minHeight || 20;
33489 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33491 // center panel can't be hidden
33495 // center panel can't be hidden
33498 getMinWidth: function(){
33499 return this.minWidth;
33502 getMinHeight: function(){
33503 return this.minHeight;
33516 Roo.bootstrap.layout.North = function(config)
33518 config.region = 'north';
33519 config.cursor = 'n-resize';
33521 Roo.bootstrap.layout.Split.call(this, config);
33525 this.split.placement = Roo.bootstrap.SplitBar.TOP;
33526 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33527 this.split.el.addClass("roo-layout-split-v");
33529 var size = config.initialSize || config.height;
33530 if(typeof size != "undefined"){
33531 this.el.setHeight(size);
33534 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33536 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33540 getBox : function(){
33541 if(this.collapsed){
33542 return this.collapsedEl.getBox();
33544 var box = this.el.getBox();
33546 box.height += this.split.el.getHeight();
33551 updateBox : function(box){
33552 if(this.split && !this.collapsed){
33553 box.height -= this.split.el.getHeight();
33554 this.split.el.setLeft(box.x);
33555 this.split.el.setTop(box.y+box.height);
33556 this.split.el.setWidth(box.width);
33558 if(this.collapsed){
33559 this.updateBody(box.width, null);
33561 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33569 Roo.bootstrap.layout.South = function(config){
33570 config.region = 'south';
33571 config.cursor = 's-resize';
33572 Roo.bootstrap.layout.Split.call(this, config);
33574 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33575 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33576 this.split.el.addClass("roo-layout-split-v");
33578 var size = config.initialSize || config.height;
33579 if(typeof size != "undefined"){
33580 this.el.setHeight(size);
33584 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33585 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33586 getBox : function(){
33587 if(this.collapsed){
33588 return this.collapsedEl.getBox();
33590 var box = this.el.getBox();
33592 var sh = this.split.el.getHeight();
33599 updateBox : function(box){
33600 if(this.split && !this.collapsed){
33601 var sh = this.split.el.getHeight();
33604 this.split.el.setLeft(box.x);
33605 this.split.el.setTop(box.y-sh);
33606 this.split.el.setWidth(box.width);
33608 if(this.collapsed){
33609 this.updateBody(box.width, null);
33611 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33615 Roo.bootstrap.layout.East = function(config){
33616 config.region = "east";
33617 config.cursor = "e-resize";
33618 Roo.bootstrap.layout.Split.call(this, config);
33620 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33621 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33622 this.split.el.addClass("roo-layout-split-h");
33624 var size = config.initialSize || config.width;
33625 if(typeof size != "undefined"){
33626 this.el.setWidth(size);
33629 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33630 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33631 getBox : function(){
33632 if(this.collapsed){
33633 return this.collapsedEl.getBox();
33635 var box = this.el.getBox();
33637 var sw = this.split.el.getWidth();
33644 updateBox : function(box){
33645 if(this.split && !this.collapsed){
33646 var sw = this.split.el.getWidth();
33648 this.split.el.setLeft(box.x);
33649 this.split.el.setTop(box.y);
33650 this.split.el.setHeight(box.height);
33653 if(this.collapsed){
33654 this.updateBody(null, box.height);
33656 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33660 Roo.bootstrap.layout.West = function(config){
33661 config.region = "west";
33662 config.cursor = "w-resize";
33664 Roo.bootstrap.layout.Split.call(this, config);
33666 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33667 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33668 this.split.el.addClass("roo-layout-split-h");
33672 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33673 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33675 onRender: function(ctr, pos)
33677 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33678 var size = this.config.initialSize || this.config.width;
33679 if(typeof size != "undefined"){
33680 this.el.setWidth(size);
33684 getBox : function(){
33685 if(this.collapsed){
33686 return this.collapsedEl.getBox();
33688 var box = this.el.getBox();
33690 box.width += this.split.el.getWidth();
33695 updateBox : function(box){
33696 if(this.split && !this.collapsed){
33697 var sw = this.split.el.getWidth();
33699 this.split.el.setLeft(box.x+box.width);
33700 this.split.el.setTop(box.y);
33701 this.split.el.setHeight(box.height);
33703 if(this.collapsed){
33704 this.updateBody(null, box.height);
33706 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33709 Roo.namespace("Roo.bootstrap.panel");/*
33711 * Ext JS Library 1.1.1
33712 * Copyright(c) 2006-2007, Ext JS, LLC.
33714 * Originally Released Under LGPL - original licence link has changed is not relivant.
33717 * <script type="text/javascript">
33720 * @class Roo.ContentPanel
33721 * @extends Roo.util.Observable
33722 * A basic ContentPanel element.
33723 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
33724 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
33725 * @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
33726 * @cfg {Boolean} closable True if the panel can be closed/removed
33727 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
33728 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33729 * @cfg {Toolbar} toolbar A toolbar for this panel
33730 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
33731 * @cfg {String} title The title for this panel
33732 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33733 * @cfg {String} url Calls {@link #setUrl} with this value
33734 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33735 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
33736 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
33737 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
33740 * Create a new ContentPanel.
33741 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33742 * @param {String/Object} config A string to set only the title or a config object
33743 * @param {String} content (optional) Set the HTML content for this panel
33744 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33746 Roo.bootstrap.panel.Content = function( config){
33748 var el = config.el;
33749 var content = config.content;
33751 if(config.autoCreate){ // xtype is available if this is called from factory
33754 this.el = Roo.get(el);
33755 if(!this.el && config && config.autoCreate){
33756 if(typeof config.autoCreate == "object"){
33757 if(!config.autoCreate.id){
33758 config.autoCreate.id = config.id||el;
33760 this.el = Roo.DomHelper.append(document.body,
33761 config.autoCreate, true);
33763 var elcfg = { tag: "div",
33764 cls: "roo-layout-inactive-content",
33768 elcfg.html = config.html;
33772 this.el = Roo.DomHelper.append(document.body, elcfg , true);
33775 this.closable = false;
33776 this.loaded = false;
33777 this.active = false;
33780 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33782 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33784 this.wrapEl = this.el.wrap();
33786 if (config.toolbar.items) {
33787 ti = config.toolbar.items ;
33788 delete config.toolbar.items ;
33792 this.toolbar.render(this.wrapEl, 'before');
33793 for(var i =0;i < ti.length;i++) {
33794 // Roo.log(['add child', items[i]]);
33795 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33797 this.toolbar.items = nitems;
33798 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33799 delete config.toolbar;
33803 // xtype created footer. - not sure if will work as we normally have to render first..
33804 if (this.footer && !this.footer.el && this.footer.xtype) {
33805 if (!this.wrapEl) {
33806 this.wrapEl = this.el.wrap();
33809 this.footer.container = this.wrapEl.createChild();
33811 this.footer = Roo.factory(this.footer, Roo);
33816 if(typeof config == "string"){
33817 this.title = config;
33819 Roo.apply(this, config);
33823 this.resizeEl = Roo.get(this.resizeEl, true);
33825 this.resizeEl = this.el;
33827 // handle view.xtype
33835 * Fires when this panel is activated.
33836 * @param {Roo.ContentPanel} this
33840 * @event deactivate
33841 * Fires when this panel is activated.
33842 * @param {Roo.ContentPanel} this
33844 "deactivate" : true,
33848 * Fires when this panel is resized if fitToFrame is true.
33849 * @param {Roo.ContentPanel} this
33850 * @param {Number} width The width after any component adjustments
33851 * @param {Number} height The height after any component adjustments
33857 * Fires when this tab is created
33858 * @param {Roo.ContentPanel} this
33869 if(this.autoScroll){
33870 this.resizeEl.setStyle("overflow", "auto");
33872 // fix randome scrolling
33873 //this.el.on('scroll', function() {
33874 // Roo.log('fix random scolling');
33875 // this.scrollTo('top',0);
33878 content = content || this.content;
33880 this.setContent(content);
33882 if(config && config.url){
33883 this.setUrl(this.url, this.params, this.loadOnce);
33888 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33890 if (this.view && typeof(this.view.xtype) != 'undefined') {
33891 this.view.el = this.el.appendChild(document.createElement("div"));
33892 this.view = Roo.factory(this.view);
33893 this.view.render && this.view.render(false, '');
33897 this.fireEvent('render', this);
33900 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33902 setRegion : function(region){
33903 this.region = region;
33905 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33907 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33912 * Returns the toolbar for this Panel if one was configured.
33913 * @return {Roo.Toolbar}
33915 getToolbar : function(){
33916 return this.toolbar;
33919 setActiveState : function(active){
33920 this.active = active;
33922 this.fireEvent("deactivate", this);
33924 this.fireEvent("activate", this);
33928 * Updates this panel's element
33929 * @param {String} content The new content
33930 * @param {Boolean} loadScripts (optional) true to look for and process scripts
33932 setContent : function(content, loadScripts){
33933 this.el.update(content, loadScripts);
33936 ignoreResize : function(w, h){
33937 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33940 this.lastSize = {width: w, height: h};
33945 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33946 * @return {Roo.UpdateManager} The UpdateManager
33948 getUpdateManager : function(){
33949 return this.el.getUpdateManager();
33952 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33953 * @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:
33956 url: "your-url.php",
33957 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33958 callback: yourFunction,
33959 scope: yourObject, //(optional scope)
33962 text: "Loading...",
33967 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33968 * 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.
33969 * @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}
33970 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33971 * @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.
33972 * @return {Roo.ContentPanel} this
33975 var um = this.el.getUpdateManager();
33976 um.update.apply(um, arguments);
33982 * 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.
33983 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33984 * @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)
33985 * @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)
33986 * @return {Roo.UpdateManager} The UpdateManager
33988 setUrl : function(url, params, loadOnce){
33989 if(this.refreshDelegate){
33990 this.removeListener("activate", this.refreshDelegate);
33992 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33993 this.on("activate", this.refreshDelegate);
33994 return this.el.getUpdateManager();
33997 _handleRefresh : function(url, params, loadOnce){
33998 if(!loadOnce || !this.loaded){
33999 var updater = this.el.getUpdateManager();
34000 updater.update(url, params, this._setLoaded.createDelegate(this));
34004 _setLoaded : function(){
34005 this.loaded = true;
34009 * Returns this panel's id
34012 getId : function(){
34017 * Returns this panel's element - used by regiosn to add.
34018 * @return {Roo.Element}
34020 getEl : function(){
34021 return this.wrapEl || this.el;
34026 adjustForComponents : function(width, height)
34028 //Roo.log('adjustForComponents ');
34029 if(this.resizeEl != this.el){
34030 width -= this.el.getFrameWidth('lr');
34031 height -= this.el.getFrameWidth('tb');
34034 var te = this.toolbar.getEl();
34035 height -= te.getHeight();
34036 te.setWidth(width);
34039 var te = this.footer.getEl();
34040 Roo.log("footer:" + te.getHeight());
34042 height -= te.getHeight();
34043 te.setWidth(width);
34047 if(this.adjustments){
34048 width += this.adjustments[0];
34049 height += this.adjustments[1];
34051 return {"width": width, "height": height};
34054 setSize : function(width, height){
34055 if(this.fitToFrame && !this.ignoreResize(width, height)){
34056 if(this.fitContainer && this.resizeEl != this.el){
34057 this.el.setSize(width, height);
34059 var size = this.adjustForComponents(width, height);
34060 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34061 this.fireEvent('resize', this, size.width, size.height);
34066 * Returns this panel's title
34069 getTitle : function(){
34074 * Set this panel's title
34075 * @param {String} title
34077 setTitle : function(title){
34078 this.title = title;
34080 this.region.updatePanelTitle(this, title);
34085 * Returns true is this panel was configured to be closable
34086 * @return {Boolean}
34088 isClosable : function(){
34089 return this.closable;
34092 beforeSlide : function(){
34094 this.resizeEl.clip();
34097 afterSlide : function(){
34099 this.resizeEl.unclip();
34103 * Force a content refresh from the URL specified in the {@link #setUrl} method.
34104 * Will fail silently if the {@link #setUrl} method has not been called.
34105 * This does not activate the panel, just updates its content.
34107 refresh : function(){
34108 if(this.refreshDelegate){
34109 this.loaded = false;
34110 this.refreshDelegate();
34115 * Destroys this panel
34117 destroy : function(){
34118 this.el.removeAllListeners();
34119 var tempEl = document.createElement("span");
34120 tempEl.appendChild(this.el.dom);
34121 tempEl.innerHTML = "";
34127 * form - if the content panel contains a form - this is a reference to it.
34128 * @type {Roo.form.Form}
34132 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34133 * This contains a reference to it.
34139 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34149 * @param {Object} cfg Xtype definition of item to add.
34153 getChildContainer: function () {
34154 return this.getEl();
34159 var ret = new Roo.factory(cfg);
34164 if (cfg.xtype.match(/^Form$/)) {
34167 //if (this.footer) {
34168 // el = this.footer.container.insertSibling(false, 'before');
34170 el = this.el.createChild();
34173 this.form = new Roo.form.Form(cfg);
34176 if ( this.form.allItems.length) {
34177 this.form.render(el.dom);
34181 // should only have one of theses..
34182 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34183 // views.. should not be just added - used named prop 'view''
34185 cfg.el = this.el.appendChild(document.createElement("div"));
34188 var ret = new Roo.factory(cfg);
34190 ret.render && ret.render(false, ''); // render blank..
34200 * @class Roo.bootstrap.panel.Grid
34201 * @extends Roo.bootstrap.panel.Content
34203 * Create a new GridPanel.
34204 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34205 * @param {Object} config A the config object
34211 Roo.bootstrap.panel.Grid = function(config)
34215 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34216 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34218 config.el = this.wrapper;
34219 //this.el = this.wrapper;
34221 if (config.container) {
34222 // ctor'ed from a Border/panel.grid
34225 this.wrapper.setStyle("overflow", "hidden");
34226 this.wrapper.addClass('roo-grid-container');
34231 if(config.toolbar){
34232 var tool_el = this.wrapper.createChild();
34233 this.toolbar = Roo.factory(config.toolbar);
34235 if (config.toolbar.items) {
34236 ti = config.toolbar.items ;
34237 delete config.toolbar.items ;
34241 this.toolbar.render(tool_el);
34242 for(var i =0;i < ti.length;i++) {
34243 // Roo.log(['add child', items[i]]);
34244 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34246 this.toolbar.items = nitems;
34248 delete config.toolbar;
34251 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34252 config.grid.scrollBody = true;;
34253 config.grid.monitorWindowResize = false; // turn off autosizing
34254 config.grid.autoHeight = false;
34255 config.grid.autoWidth = false;
34257 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34259 if (config.background) {
34260 // render grid on panel activation (if panel background)
34261 this.on('activate', function(gp) {
34262 if (!gp.grid.rendered) {
34263 gp.grid.render(el);
34264 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34270 this.grid.render(this.wrapper);
34271 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34274 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34275 // ??? needed ??? config.el = this.wrapper;
34280 // xtype created footer. - not sure if will work as we normally have to render first..
34281 if (this.footer && !this.footer.el && this.footer.xtype) {
34283 var ctr = this.grid.getView().getFooterPanel(true);
34284 this.footer.dataSource = this.grid.dataSource;
34285 this.footer = Roo.factory(this.footer, Roo);
34286 this.footer.render(ctr);
34296 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34297 getId : function(){
34298 return this.grid.id;
34302 * Returns the grid for this panel
34303 * @return {Roo.bootstrap.Table}
34305 getGrid : function(){
34309 setSize : function(width, height){
34310 if(!this.ignoreResize(width, height)){
34311 var grid = this.grid;
34312 var size = this.adjustForComponents(width, height);
34313 var gridel = grid.getGridEl();
34314 gridel.setSize(size.width, size.height);
34316 var thd = grid.getGridEl().select('thead',true).first();
34317 var tbd = grid.getGridEl().select('tbody', true).first();
34319 tbd.setSize(width, height - thd.getHeight());
34328 beforeSlide : function(){
34329 this.grid.getView().scroller.clip();
34332 afterSlide : function(){
34333 this.grid.getView().scroller.unclip();
34336 destroy : function(){
34337 this.grid.destroy();
34339 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
34344 * @class Roo.bootstrap.panel.Nest
34345 * @extends Roo.bootstrap.panel.Content
34347 * Create a new Panel, that can contain a layout.Border.
34350 * @param {Roo.BorderLayout} layout The layout for this panel
34351 * @param {String/Object} config A string to set only the title or a config object
34353 Roo.bootstrap.panel.Nest = function(config)
34355 // construct with only one argument..
34356 /* FIXME - implement nicer consturctors
34357 if (layout.layout) {
34359 layout = config.layout;
34360 delete config.layout;
34362 if (layout.xtype && !layout.getEl) {
34363 // then layout needs constructing..
34364 layout = Roo.factory(layout, Roo);
34368 config.el = config.layout.getEl();
34370 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34372 config.layout.monitorWindowResize = false; // turn off autosizing
34373 this.layout = config.layout;
34374 this.layout.getEl().addClass("roo-layout-nested-layout");
34381 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34383 setSize : function(width, height){
34384 if(!this.ignoreResize(width, height)){
34385 var size = this.adjustForComponents(width, height);
34386 var el = this.layout.getEl();
34387 el.setSize(size.width, size.height);
34388 var touch = el.dom.offsetWidth;
34389 this.layout.layout();
34390 // ie requires a double layout on the first pass
34391 if(Roo.isIE && !this.initialized){
34392 this.initialized = true;
34393 this.layout.layout();
34398 // activate all subpanels if not currently active..
34400 setActiveState : function(active){
34401 this.active = active;
34403 this.fireEvent("deactivate", this);
34407 this.fireEvent("activate", this);
34408 // not sure if this should happen before or after..
34409 if (!this.layout) {
34410 return; // should not happen..
34413 for (var r in this.layout.regions) {
34414 reg = this.layout.getRegion(r);
34415 if (reg.getActivePanel()) {
34416 //reg.showPanel(reg.getActivePanel()); // force it to activate..
34417 reg.setActivePanel(reg.getActivePanel());
34420 if (!reg.panels.length) {
34423 reg.showPanel(reg.getPanel(0));
34432 * Returns the nested BorderLayout for this panel
34433 * @return {Roo.BorderLayout}
34435 getLayout : function(){
34436 return this.layout;
34440 * Adds a xtype elements to the layout of the nested panel
34444 xtype : 'ContentPanel',
34451 xtype : 'NestedLayoutPanel',
34457 items : [ ... list of content panels or nested layout panels.. ]
34461 * @param {Object} cfg Xtype definition of item to add.
34463 addxtype : function(cfg) {
34464 return this.layout.addxtype(cfg);
34469 * Ext JS Library 1.1.1
34470 * Copyright(c) 2006-2007, Ext JS, LLC.
34472 * Originally Released Under LGPL - original licence link has changed is not relivant.
34475 * <script type="text/javascript">
34478 * @class Roo.TabPanel
34479 * @extends Roo.util.Observable
34480 * A lightweight tab container.
34484 // basic tabs 1, built from existing content
34485 var tabs = new Roo.TabPanel("tabs1");
34486 tabs.addTab("script", "View Script");
34487 tabs.addTab("markup", "View Markup");
34488 tabs.activate("script");
34490 // more advanced tabs, built from javascript
34491 var jtabs = new Roo.TabPanel("jtabs");
34492 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34494 // set up the UpdateManager
34495 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34496 var updater = tab2.getUpdateManager();
34497 updater.setDefaultUrl("ajax1.htm");
34498 tab2.on('activate', updater.refresh, updater, true);
34500 // Use setUrl for Ajax loading
34501 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34502 tab3.setUrl("ajax2.htm", null, true);
34505 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34508 jtabs.activate("jtabs-1");
34511 * Create a new TabPanel.
34512 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34513 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34515 Roo.bootstrap.panel.Tabs = function(config){
34517 * The container element for this TabPanel.
34518 * @type Roo.Element
34520 this.el = Roo.get(config.el);
34523 if(typeof config == "boolean"){
34524 this.tabPosition = config ? "bottom" : "top";
34526 Roo.apply(this, config);
34530 if(this.tabPosition == "bottom"){
34531 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34532 this.el.addClass("roo-tabs-bottom");
34534 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34535 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34536 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34538 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34540 if(this.tabPosition != "bottom"){
34541 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34542 * @type Roo.Element
34544 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34545 this.el.addClass("roo-tabs-top");
34549 this.bodyEl.setStyle("position", "relative");
34551 this.active = null;
34552 this.activateDelegate = this.activate.createDelegate(this);
34557 * Fires when the active tab changes
34558 * @param {Roo.TabPanel} this
34559 * @param {Roo.TabPanelItem} activePanel The new active tab
34563 * @event beforetabchange
34564 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34565 * @param {Roo.TabPanel} this
34566 * @param {Object} e Set cancel to true on this object to cancel the tab change
34567 * @param {Roo.TabPanelItem} tab The tab being changed to
34569 "beforetabchange" : true
34572 Roo.EventManager.onWindowResize(this.onResize, this);
34573 this.cpad = this.el.getPadding("lr");
34574 this.hiddenCount = 0;
34577 // toolbar on the tabbar support...
34578 if (this.toolbar) {
34579 alert("no toolbar support yet");
34580 this.toolbar = false;
34582 var tcfg = this.toolbar;
34583 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
34584 this.toolbar = new Roo.Toolbar(tcfg);
34585 if (Roo.isSafari) {
34586 var tbl = tcfg.container.child('table', true);
34587 tbl.setAttribute('width', '100%');
34595 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34598 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34600 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34602 tabPosition : "top",
34604 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34606 currentTabWidth : 0,
34608 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34612 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34616 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34618 preferredTabWidth : 175,
34620 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34622 resizeTabs : false,
34624 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34626 monitorResize : true,
34628 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
34633 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34634 * @param {String} id The id of the div to use <b>or create</b>
34635 * @param {String} text The text for the tab
34636 * @param {String} content (optional) Content to put in the TabPanelItem body
34637 * @param {Boolean} closable (optional) True to create a close icon on the tab
34638 * @return {Roo.TabPanelItem} The created TabPanelItem
34640 addTab : function(id, text, content, closable)
34642 var item = new Roo.bootstrap.panel.TabItem({
34646 closable : closable
34648 this.addTabItem(item);
34650 item.setContent(content);
34656 * Returns the {@link Roo.TabPanelItem} with the specified id/index
34657 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34658 * @return {Roo.TabPanelItem}
34660 getTab : function(id){
34661 return this.items[id];
34665 * Hides the {@link Roo.TabPanelItem} with the specified id/index
34666 * @param {String/Number} id The id or index of the TabPanelItem to hide.
34668 hideTab : function(id){
34669 var t = this.items[id];
34672 this.hiddenCount++;
34673 this.autoSizeTabs();
34678 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34679 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34681 unhideTab : function(id){
34682 var t = this.items[id];
34684 t.setHidden(false);
34685 this.hiddenCount--;
34686 this.autoSizeTabs();
34691 * Adds an existing {@link Roo.TabPanelItem}.
34692 * @param {Roo.TabPanelItem} item The TabPanelItem to add
34694 addTabItem : function(item){
34695 this.items[item.id] = item;
34696 this.items.push(item);
34697 // if(this.resizeTabs){
34698 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34699 // this.autoSizeTabs();
34701 // item.autoSize();
34706 * Removes a {@link Roo.TabPanelItem}.
34707 * @param {String/Number} id The id or index of the TabPanelItem to remove.
34709 removeTab : function(id){
34710 var items = this.items;
34711 var tab = items[id];
34712 if(!tab) { return; }
34713 var index = items.indexOf(tab);
34714 if(this.active == tab && items.length > 1){
34715 var newTab = this.getNextAvailable(index);
34720 this.stripEl.dom.removeChild(tab.pnode.dom);
34721 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34722 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34724 items.splice(index, 1);
34725 delete this.items[tab.id];
34726 tab.fireEvent("close", tab);
34727 tab.purgeListeners();
34728 this.autoSizeTabs();
34731 getNextAvailable : function(start){
34732 var items = this.items;
34734 // look for a next tab that will slide over to
34735 // replace the one being removed
34736 while(index < items.length){
34737 var item = items[++index];
34738 if(item && !item.isHidden()){
34742 // if one isn't found select the previous tab (on the left)
34745 var item = items[--index];
34746 if(item && !item.isHidden()){
34754 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34755 * @param {String/Number} id The id or index of the TabPanelItem to disable.
34757 disableTab : function(id){
34758 var tab = this.items[id];
34759 if(tab && this.active != tab){
34765 * Enables a {@link Roo.TabPanelItem} that is disabled.
34766 * @param {String/Number} id The id or index of the TabPanelItem to enable.
34768 enableTab : function(id){
34769 var tab = this.items[id];
34774 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34775 * @param {String/Number} id The id or index of the TabPanelItem to activate.
34776 * @return {Roo.TabPanelItem} The TabPanelItem.
34778 activate : function(id){
34779 var tab = this.items[id];
34783 if(tab == this.active || tab.disabled){
34787 this.fireEvent("beforetabchange", this, e, tab);
34788 if(e.cancel !== true && !tab.disabled){
34790 this.active.hide();
34792 this.active = this.items[id];
34793 this.active.show();
34794 this.fireEvent("tabchange", this, this.active);
34800 * Gets the active {@link Roo.TabPanelItem}.
34801 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34803 getActiveTab : function(){
34804 return this.active;
34808 * Updates the tab body element to fit the height of the container element
34809 * for overflow scrolling
34810 * @param {Number} targetHeight (optional) Override the starting height from the elements height
34812 syncHeight : function(targetHeight){
34813 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34814 var bm = this.bodyEl.getMargins();
34815 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34816 this.bodyEl.setHeight(newHeight);
34820 onResize : function(){
34821 if(this.monitorResize){
34822 this.autoSizeTabs();
34827 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34829 beginUpdate : function(){
34830 this.updating = true;
34834 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34836 endUpdate : function(){
34837 this.updating = false;
34838 this.autoSizeTabs();
34842 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34844 autoSizeTabs : function(){
34845 var count = this.items.length;
34846 var vcount = count - this.hiddenCount;
34847 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34850 var w = Math.max(this.el.getWidth() - this.cpad, 10);
34851 var availWidth = Math.floor(w / vcount);
34852 var b = this.stripBody;
34853 if(b.getWidth() > w){
34854 var tabs = this.items;
34855 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34856 if(availWidth < this.minTabWidth){
34857 /*if(!this.sleft){ // incomplete scrolling code
34858 this.createScrollButtons();
34861 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34864 if(this.currentTabWidth < this.preferredTabWidth){
34865 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34871 * Returns the number of tabs in this TabPanel.
34874 getCount : function(){
34875 return this.items.length;
34879 * Resizes all the tabs to the passed width
34880 * @param {Number} The new width
34882 setTabWidth : function(width){
34883 this.currentTabWidth = width;
34884 for(var i = 0, len = this.items.length; i < len; i++) {
34885 if(!this.items[i].isHidden()) {
34886 this.items[i].setWidth(width);
34892 * Destroys this TabPanel
34893 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34895 destroy : function(removeEl){
34896 Roo.EventManager.removeResizeListener(this.onResize, this);
34897 for(var i = 0, len = this.items.length; i < len; i++){
34898 this.items[i].purgeListeners();
34900 if(removeEl === true){
34901 this.el.update("");
34906 createStrip : function(container)
34908 var strip = document.createElement("nav");
34909 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34910 container.appendChild(strip);
34914 createStripList : function(strip)
34916 // div wrapper for retard IE
34917 // returns the "tr" element.
34918 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34919 //'<div class="x-tabs-strip-wrap">'+
34920 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34921 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34922 return strip.firstChild; //.firstChild.firstChild.firstChild;
34924 createBody : function(container)
34926 var body = document.createElement("div");
34927 Roo.id(body, "tab-body");
34928 //Roo.fly(body).addClass("x-tabs-body");
34929 Roo.fly(body).addClass("tab-content");
34930 container.appendChild(body);
34933 createItemBody :function(bodyEl, id){
34934 var body = Roo.getDom(id);
34936 body = document.createElement("div");
34939 //Roo.fly(body).addClass("x-tabs-item-body");
34940 Roo.fly(body).addClass("tab-pane");
34941 bodyEl.insertBefore(body, bodyEl.firstChild);
34945 createStripElements : function(stripEl, text, closable)
34947 var td = document.createElement("li"); // was td..
34948 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34949 //stripEl.appendChild(td);
34951 td.className = "x-tabs-closable";
34952 if(!this.closeTpl){
34953 this.closeTpl = new Roo.Template(
34954 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34955 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34956 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
34959 var el = this.closeTpl.overwrite(td, {"text": text});
34960 var close = el.getElementsByTagName("div")[0];
34961 var inner = el.getElementsByTagName("em")[0];
34962 return {"el": el, "close": close, "inner": inner};
34965 // not sure what this is..
34967 //this.tabTpl = new Roo.Template(
34968 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34969 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34971 this.tabTpl = new Roo.Template(
34973 '<span unselectable="on"' +
34974 (this.disableTooltips ? '' : ' title="{text}"') +
34975 ' >{text}</span></span></a>'
34979 var el = this.tabTpl.overwrite(td, {"text": text});
34980 var inner = el.getElementsByTagName("span")[0];
34981 return {"el": el, "inner": inner};
34989 * @class Roo.TabPanelItem
34990 * @extends Roo.util.Observable
34991 * Represents an individual item (tab plus body) in a TabPanel.
34992 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34993 * @param {String} id The id of this TabPanelItem
34994 * @param {String} text The text for the tab of this TabPanelItem
34995 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34997 Roo.bootstrap.panel.TabItem = function(config){
34999 * The {@link Roo.TabPanel} this TabPanelItem belongs to
35000 * @type Roo.TabPanel
35002 this.tabPanel = config.panel;
35004 * The id for this TabPanelItem
35007 this.id = config.id;
35009 this.disabled = false;
35011 this.text = config.text;
35013 this.loaded = false;
35014 this.closable = config.closable;
35017 * The body element for this TabPanelItem.
35018 * @type Roo.Element
35020 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35021 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35022 this.bodyEl.setStyle("display", "block");
35023 this.bodyEl.setStyle("zoom", "1");
35024 //this.hideAction();
35026 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35028 this.el = Roo.get(els.el);
35029 this.inner = Roo.get(els.inner, true);
35030 this.textEl = Roo.get(this.el.dom.firstChild, true);
35031 this.pnode = Roo.get(els.el.parentNode, true);
35032 this.el.on("mousedown", this.onTabMouseDown, this);
35033 this.el.on("click", this.onTabClick, this);
35035 if(config.closable){
35036 var c = Roo.get(els.close, true);
35037 c.dom.title = this.closeText;
35038 c.addClassOnOver("close-over");
35039 c.on("click", this.closeClick, this);
35045 * Fires when this tab becomes the active tab.
35046 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35047 * @param {Roo.TabPanelItem} this
35051 * @event beforeclose
35052 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35053 * @param {Roo.TabPanelItem} this
35054 * @param {Object} e Set cancel to true on this object to cancel the close.
35056 "beforeclose": true,
35059 * Fires when this tab is closed.
35060 * @param {Roo.TabPanelItem} this
35064 * @event deactivate
35065 * Fires when this tab is no longer the active tab.
35066 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35067 * @param {Roo.TabPanelItem} this
35069 "deactivate" : true
35071 this.hidden = false;
35073 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35076 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35078 purgeListeners : function(){
35079 Roo.util.Observable.prototype.purgeListeners.call(this);
35080 this.el.removeAllListeners();
35083 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35086 this.pnode.addClass("active");
35089 this.tabPanel.stripWrap.repaint();
35091 this.fireEvent("activate", this.tabPanel, this);
35095 * Returns true if this tab is the active tab.
35096 * @return {Boolean}
35098 isActive : function(){
35099 return this.tabPanel.getActiveTab() == this;
35103 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35106 this.pnode.removeClass("active");
35108 this.fireEvent("deactivate", this.tabPanel, this);
35111 hideAction : function(){
35112 this.bodyEl.hide();
35113 this.bodyEl.setStyle("position", "absolute");
35114 this.bodyEl.setLeft("-20000px");
35115 this.bodyEl.setTop("-20000px");
35118 showAction : function(){
35119 this.bodyEl.setStyle("position", "relative");
35120 this.bodyEl.setTop("");
35121 this.bodyEl.setLeft("");
35122 this.bodyEl.show();
35126 * Set the tooltip for the tab.
35127 * @param {String} tooltip The tab's tooltip
35129 setTooltip : function(text){
35130 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35131 this.textEl.dom.qtip = text;
35132 this.textEl.dom.removeAttribute('title');
35134 this.textEl.dom.title = text;
35138 onTabClick : function(e){
35139 e.preventDefault();
35140 this.tabPanel.activate(this.id);
35143 onTabMouseDown : function(e){
35144 e.preventDefault();
35145 this.tabPanel.activate(this.id);
35148 getWidth : function(){
35149 return this.inner.getWidth();
35152 setWidth : function(width){
35153 var iwidth = width - this.pnode.getPadding("lr");
35154 this.inner.setWidth(iwidth);
35155 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35156 this.pnode.setWidth(width);
35160 * Show or hide the tab
35161 * @param {Boolean} hidden True to hide or false to show.
35163 setHidden : function(hidden){
35164 this.hidden = hidden;
35165 this.pnode.setStyle("display", hidden ? "none" : "");
35169 * Returns true if this tab is "hidden"
35170 * @return {Boolean}
35172 isHidden : function(){
35173 return this.hidden;
35177 * Returns the text for this tab
35180 getText : function(){
35184 autoSize : function(){
35185 //this.el.beginMeasure();
35186 this.textEl.setWidth(1);
35188 * #2804 [new] Tabs in Roojs
35189 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35191 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35192 //this.el.endMeasure();
35196 * Sets the text for the tab (Note: this also sets the tooltip text)
35197 * @param {String} text The tab's text and tooltip
35199 setText : function(text){
35201 this.textEl.update(text);
35202 this.setTooltip(text);
35203 //if(!this.tabPanel.resizeTabs){
35204 // this.autoSize();
35208 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35210 activate : function(){
35211 this.tabPanel.activate(this.id);
35215 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35217 disable : function(){
35218 if(this.tabPanel.active != this){
35219 this.disabled = true;
35220 this.pnode.addClass("disabled");
35225 * Enables this TabPanelItem if it was previously disabled.
35227 enable : function(){
35228 this.disabled = false;
35229 this.pnode.removeClass("disabled");
35233 * Sets the content for this TabPanelItem.
35234 * @param {String} content The content
35235 * @param {Boolean} loadScripts true to look for and load scripts
35237 setContent : function(content, loadScripts){
35238 this.bodyEl.update(content, loadScripts);
35242 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35243 * @return {Roo.UpdateManager} The UpdateManager
35245 getUpdateManager : function(){
35246 return this.bodyEl.getUpdateManager();
35250 * Set a URL to be used to load the content for this TabPanelItem.
35251 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35252 * @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)
35253 * @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)
35254 * @return {Roo.UpdateManager} The UpdateManager
35256 setUrl : function(url, params, loadOnce){
35257 if(this.refreshDelegate){
35258 this.un('activate', this.refreshDelegate);
35260 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35261 this.on("activate", this.refreshDelegate);
35262 return this.bodyEl.getUpdateManager();
35266 _handleRefresh : function(url, params, loadOnce){
35267 if(!loadOnce || !this.loaded){
35268 var updater = this.bodyEl.getUpdateManager();
35269 updater.update(url, params, this._setLoaded.createDelegate(this));
35274 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
35275 * Will fail silently if the setUrl method has not been called.
35276 * This does not activate the panel, just updates its content.
35278 refresh : function(){
35279 if(this.refreshDelegate){
35280 this.loaded = false;
35281 this.refreshDelegate();
35286 _setLoaded : function(){
35287 this.loaded = true;
35291 closeClick : function(e){
35294 this.fireEvent("beforeclose", this, o);
35295 if(o.cancel !== true){
35296 this.tabPanel.removeTab(this.id);
35300 * The text displayed in the tooltip for the close icon.
35303 closeText : "Close this tab"