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;
5716 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5718 this.sm.grid = this;
5719 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5720 this.sm = this.selModel;
5721 this.sm.xmodule = this.xmodule || false;
5724 if (this.cm && typeof(this.cm.config) == 'undefined') {
5725 this.colModel = new Roo.grid.ColumnModel(this.cm);
5726 this.cm = this.colModel;
5727 this.cm.xmodule = this.xmodule || false;
5730 this.store= Roo.factory(this.store, Roo.data);
5731 this.ds = this.store;
5732 this.ds.xmodule = this.xmodule || false;
5735 if (this.footer && this.store) {
5736 this.footer.dataSource = this.ds;
5737 this.footer = Roo.factory(this.footer);
5744 * Fires when a cell is clicked
5745 * @param {Roo.bootstrap.Table} this
5746 * @param {Roo.Element} el
5747 * @param {Number} rowIndex
5748 * @param {Number} columnIndex
5749 * @param {Roo.EventObject} e
5753 * @event celldblclick
5754 * Fires when a cell is double clicked
5755 * @param {Roo.bootstrap.Table} this
5756 * @param {Roo.Element} el
5757 * @param {Number} rowIndex
5758 * @param {Number} columnIndex
5759 * @param {Roo.EventObject} e
5761 "celldblclick" : true,
5764 * Fires when a row is clicked
5765 * @param {Roo.bootstrap.Table} this
5766 * @param {Roo.Element} el
5767 * @param {Number} rowIndex
5768 * @param {Roo.EventObject} e
5772 * @event rowdblclick
5773 * Fires when a row is double clicked
5774 * @param {Roo.bootstrap.Table} this
5775 * @param {Roo.Element} el
5776 * @param {Number} rowIndex
5777 * @param {Roo.EventObject} e
5779 "rowdblclick" : true,
5782 * Fires when a mouseover occur
5783 * @param {Roo.bootstrap.Table} this
5784 * @param {Roo.Element} el
5785 * @param {Number} rowIndex
5786 * @param {Number} columnIndex
5787 * @param {Roo.EventObject} e
5792 * Fires when a mouseout occur
5793 * @param {Roo.bootstrap.Table} this
5794 * @param {Roo.Element} el
5795 * @param {Number} rowIndex
5796 * @param {Number} columnIndex
5797 * @param {Roo.EventObject} e
5802 * Fires when a row is rendered, so you can change add a style to it.
5803 * @param {Roo.bootstrap.Table} this
5804 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5808 * @event rowsrendered
5809 * Fires when all the rows have been rendered
5810 * @param {Roo.bootstrap.Table} this
5812 'rowsrendered' : true,
5814 * @event contextmenu
5815 * The raw contextmenu event for the entire grid.
5816 * @param {Roo.EventObject} e
5818 "contextmenu" : true,
5820 * @event rowcontextmenu
5821 * Fires when a row is right clicked
5822 * @param {Roo.bootstrap.Table} this
5823 * @param {Number} rowIndex
5824 * @param {Roo.EventObject} e
5826 "rowcontextmenu" : true,
5828 * @event cellcontextmenu
5829 * Fires when a cell is right clicked
5830 * @param {Roo.bootstrap.Table} this
5831 * @param {Number} rowIndex
5832 * @param {Number} cellIndex
5833 * @param {Roo.EventObject} e
5835 "cellcontextmenu" : true,
5837 * @event headercontextmenu
5838 * Fires when a header is right clicked
5839 * @param {Roo.bootstrap.Table} this
5840 * @param {Number} columnIndex
5841 * @param {Roo.EventObject} e
5843 "headercontextmenu" : true
5847 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5873 rowSelection : false,
5874 cellSelection : false,
5877 // Roo.Element - the tbody
5879 // Roo.Element - thead element
5882 container: false, // used by gridpanel...
5884 getAutoCreate : function()
5886 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5893 if (this.scrollBody) {
5894 cfg.cls += ' table-body-fixed';
5897 cfg.cls += ' table-striped';
5901 cfg.cls += ' table-hover';
5903 if (this.bordered) {
5904 cfg.cls += ' table-bordered';
5906 if (this.condensed) {
5907 cfg.cls += ' table-condensed';
5909 if (this.responsive) {
5910 cfg.cls += ' table-responsive';
5914 cfg.cls+= ' ' +this.cls;
5917 // this lot should be simplifed...
5920 cfg.align=this.align;
5923 cfg.bgcolor=this.bgcolor;
5926 cfg.border=this.border;
5928 if (this.cellpadding) {
5929 cfg.cellpadding=this.cellpadding;
5931 if (this.cellspacing) {
5932 cfg.cellspacing=this.cellspacing;
5935 cfg.frame=this.frame;
5938 cfg.rules=this.rules;
5940 if (this.sortable) {
5941 cfg.sortable=this.sortable;
5944 cfg.summary=this.summary;
5947 cfg.width=this.width;
5950 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5953 if(this.store || this.cm){
5954 if(this.headerShow){
5955 cfg.cn.push(this.renderHeader());
5958 cfg.cn.push(this.renderBody());
5960 if(this.footerShow){
5961 cfg.cn.push(this.renderFooter());
5963 // where does this come from?
5964 //cfg.cls+= ' TableGrid';
5967 return { cn : [ cfg ] };
5970 initEvents : function()
5972 if(!this.store || !this.cm){
5976 //Roo.log('initEvents with ds!!!!');
5978 this.mainBody = this.el.select('tbody', true).first();
5979 this.mainHead = this.el.select('thead', true).first();
5985 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5986 e.on('click', _this.sort, _this);
5989 this.el.on("click", this.onClick, this);
5990 this.el.on("dblclick", this.onDblClick, this);
5992 // why is this done????? = it breaks dialogs??
5993 //this.parent().el.setStyle('position', 'relative');
5997 this.footer.parentId = this.id;
5998 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6001 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6003 this.store.on('load', this.onLoad, this);
6004 this.store.on('beforeload', this.onBeforeLoad, this);
6005 this.store.on('update', this.onUpdate, this);
6006 this.store.on('add', this.onAdd, this);
6007 this.store.on("clear", this.clear, this);
6009 this.el.on("contextmenu", this.onContextMenu, this);
6011 this.mainBody.on('scroll', this.onBodyScroll, this);
6016 onContextMenu : function(e, t)
6018 this.processEvent("contextmenu", e);
6021 processEvent : function(name, e)
6023 if (name != 'touchstart' ) {
6024 this.fireEvent(name, e);
6027 var t = e.getTarget();
6029 var cell = Roo.get(t);
6035 if(cell.findParent('tfoot', false, true)){
6039 if(cell.findParent('thead', false, true)){
6041 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6042 cell = Roo.get(t).findParent('th', false, true);
6044 Roo.log("failed to find th in thead?");
6045 Roo.log(e.getTarget());
6050 var cellIndex = cell.dom.cellIndex;
6052 var ename = name == 'touchstart' ? 'click' : name;
6053 this.fireEvent("header" + ename, this, cellIndex, e);
6058 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6059 cell = Roo.get(t).findParent('td', false, true);
6061 Roo.log("failed to find th in tbody?");
6062 Roo.log(e.getTarget());
6067 var row = cell.findParent('tr', false, true);
6068 var cellIndex = cell.dom.cellIndex;
6069 var rowIndex = row.dom.rowIndex - 1;
6073 this.fireEvent("row" + name, this, rowIndex, e);
6077 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6083 onMouseover : function(e, el)
6085 var cell = Roo.get(el);
6091 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6092 cell = cell.findParent('td', false, true);
6095 var row = cell.findParent('tr', false, true);
6096 var cellIndex = cell.dom.cellIndex;
6097 var rowIndex = row.dom.rowIndex - 1; // start from 0
6099 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6103 onMouseout : function(e, el)
6105 var cell = Roo.get(el);
6111 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6112 cell = cell.findParent('td', false, true);
6115 var row = cell.findParent('tr', false, true);
6116 var cellIndex = cell.dom.cellIndex;
6117 var rowIndex = row.dom.rowIndex - 1; // start from 0
6119 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6123 onClick : function(e, el)
6125 var cell = Roo.get(el);
6127 if(!cell || (!this.cellSelection && !this.rowSelection)){
6131 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6132 cell = cell.findParent('td', false, true);
6135 if(!cell || typeof(cell) == 'undefined'){
6139 var row = cell.findParent('tr', false, true);
6141 if(!row || typeof(row) == 'undefined'){
6145 var cellIndex = cell.dom.cellIndex;
6146 var rowIndex = this.getRowIndex(row);
6148 // why??? - should these not be based on SelectionModel?
6149 if(this.cellSelection){
6150 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6153 if(this.rowSelection){
6154 this.fireEvent('rowclick', this, row, rowIndex, e);
6160 onDblClick : function(e,el)
6162 var cell = Roo.get(el);
6164 if(!cell || (!this.CellSelection && !this.RowSelection)){
6168 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6169 cell = cell.findParent('td', false, true);
6172 if(!cell || typeof(cell) == 'undefined'){
6176 var row = cell.findParent('tr', false, true);
6178 if(!row || typeof(row) == 'undefined'){
6182 var cellIndex = cell.dom.cellIndex;
6183 var rowIndex = this.getRowIndex(row);
6185 if(this.CellSelection){
6186 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6189 if(this.RowSelection){
6190 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6194 sort : function(e,el)
6196 var col = Roo.get(el);
6198 if(!col.hasClass('sortable')){
6202 var sort = col.attr('sort');
6205 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6209 this.store.sortInfo = {field : sort, direction : dir};
6212 Roo.log("calling footer first");
6213 this.footer.onClick('first');
6216 this.store.load({ params : { start : 0 } });
6220 renderHeader : function()
6228 this.totalWidth = 0;
6230 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6232 var config = cm.config[i];
6237 html: cm.getColumnHeader(i)
6242 if(typeof(config.sortable) != 'undefined' && config.sortable){
6244 c.html = '<i class="glyphicon"></i>' + c.html;
6247 if(typeof(config.lgHeader) != 'undefined'){
6248 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6251 if(typeof(config.mdHeader) != 'undefined'){
6252 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6255 if(typeof(config.smHeader) != 'undefined'){
6256 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6259 if(typeof(config.xsHeader) != 'undefined'){
6260 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6267 if(typeof(config.tooltip) != 'undefined'){
6268 c.tooltip = config.tooltip;
6271 if(typeof(config.colspan) != 'undefined'){
6272 c.colspan = config.colspan;
6275 if(typeof(config.hidden) != 'undefined' && config.hidden){
6276 c.style += ' display:none;';
6279 if(typeof(config.dataIndex) != 'undefined'){
6280 c.sort = config.dataIndex;
6285 if(typeof(config.align) != 'undefined' && config.align.length){
6286 c.style += ' text-align:' + config.align + ';';
6289 if(typeof(config.width) != 'undefined'){
6290 c.style += ' width:' + config.width + 'px;';
6291 this.totalWidth += config.width;
6293 this.totalWidth += 100; // assume minimum of 100 per column?
6296 if(typeof(config.cls) != 'undefined'){
6297 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6300 ['xs','sm','md','lg'].map(function(size){
6302 if(typeof(config[size]) == 'undefined'){
6306 if (!config[size]) { // 0 = hidden
6307 c.cls += ' hidden-' + size;
6311 c.cls += ' col-' + size + '-' + config[size];
6321 renderBody : function()
6331 colspan : this.cm.getColumnCount()
6341 renderFooter : function()
6351 colspan : this.cm.getColumnCount()
6365 // Roo.log('ds onload');
6370 var ds = this.store;
6372 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6373 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6374 if (_this.store.sortInfo) {
6376 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6377 e.select('i', true).addClass(['glyphicon-arrow-up']);
6380 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6381 e.select('i', true).addClass(['glyphicon-arrow-down']);
6386 var tbody = this.mainBody;
6388 if(ds.getCount() > 0){
6389 ds.data.each(function(d,rowIndex){
6390 var row = this.renderRow(cm, ds, rowIndex);
6392 tbody.createChild(row);
6396 if(row.cellObjects.length){
6397 Roo.each(row.cellObjects, function(r){
6398 _this.renderCellObject(r);
6405 Roo.each(this.el.select('tbody td', true).elements, function(e){
6406 e.on('mouseover', _this.onMouseover, _this);
6409 Roo.each(this.el.select('tbody td', true).elements, function(e){
6410 e.on('mouseout', _this.onMouseout, _this);
6412 this.fireEvent('rowsrendered', this);
6413 //if(this.loadMask){
6414 // this.maskEl.hide();
6421 onUpdate : function(ds,record)
6423 this.refreshRow(record);
6426 onRemove : function(ds, record, index, isUpdate){
6427 if(isUpdate !== true){
6428 this.fireEvent("beforerowremoved", this, index, record);
6430 var bt = this.mainBody.dom;
6432 var rows = this.el.select('tbody > tr', true).elements;
6434 if(typeof(rows[index]) != 'undefined'){
6435 bt.removeChild(rows[index].dom);
6438 // if(bt.rows[index]){
6439 // bt.removeChild(bt.rows[index]);
6442 if(isUpdate !== true){
6443 //this.stripeRows(index);
6444 //this.syncRowHeights(index, index);
6446 this.fireEvent("rowremoved", this, index, record);
6450 onAdd : function(ds, records, rowIndex)
6452 //Roo.log('on Add called');
6453 // - note this does not handle multiple adding very well..
6454 var bt = this.mainBody.dom;
6455 for (var i =0 ; i < records.length;i++) {
6456 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6457 //Roo.log(records[i]);
6458 //Roo.log(this.store.getAt(rowIndex+i));
6459 this.insertRow(this.store, rowIndex + i, false);
6466 refreshRow : function(record){
6467 var ds = this.store, index;
6468 if(typeof record == 'number'){
6470 record = ds.getAt(index);
6472 index = ds.indexOf(record);
6474 this.insertRow(ds, index, true);
6475 this.onRemove(ds, record, index+1, true);
6476 //this.syncRowHeights(index, index);
6478 this.fireEvent("rowupdated", this, index, record);
6481 insertRow : function(dm, rowIndex, isUpdate){
6484 this.fireEvent("beforerowsinserted", this, rowIndex);
6486 //var s = this.getScrollState();
6487 var row = this.renderRow(this.cm, this.store, rowIndex);
6488 // insert before rowIndex..
6489 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6493 if(row.cellObjects.length){
6494 Roo.each(row.cellObjects, function(r){
6495 _this.renderCellObject(r);
6500 this.fireEvent("rowsinserted", this, rowIndex);
6501 //this.syncRowHeights(firstRow, lastRow);
6502 //this.stripeRows(firstRow);
6509 getRowDom : function(rowIndex)
6511 var rows = this.el.select('tbody > tr', true).elements;
6513 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6516 // returns the object tree for a tr..
6519 renderRow : function(cm, ds, rowIndex)
6522 var d = ds.getAt(rowIndex);
6529 var cellObjects = [];
6531 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6532 var config = cm.config[i];
6534 var renderer = cm.getRenderer(i);
6538 if(typeof(renderer) !== 'undefined'){
6539 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6541 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6542 // and are rendered into the cells after the row is rendered - using the id for the element.
6544 if(typeof(value) === 'object'){
6554 rowIndex : rowIndex,
6559 this.fireEvent('rowclass', this, rowcfg);
6563 cls : rowcfg.rowClass,
6565 html: (typeof(value) === 'object') ? '' : value
6572 if(typeof(config.colspan) != 'undefined'){
6573 td.colspan = config.colspan;
6576 if(typeof(config.hidden) != 'undefined' && config.hidden){
6577 td.style += ' display:none;';
6580 if(typeof(config.align) != 'undefined' && config.align.length){
6581 td.style += ' text-align:' + config.align + ';';
6584 if(typeof(config.width) != 'undefined'){
6585 td.style += ' width:' + config.width + 'px;';
6588 if(typeof(config.cursor) != 'undefined'){
6589 td.style += ' cursor:' + config.cursor + ';';
6592 if(typeof(config.cls) != 'undefined'){
6593 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6596 ['xs','sm','md','lg'].map(function(size){
6598 if(typeof(config[size]) == 'undefined'){
6602 if (!config[size]) { // 0 = hidden
6603 td.cls += ' hidden-' + size;
6607 td.cls += ' col-' + size + '-' + config[size];
6615 row.cellObjects = cellObjects;
6623 onBeforeLoad : function()
6625 //Roo.log('ds onBeforeLoad');
6629 //if(this.loadMask){
6630 // this.maskEl.show();
6638 this.el.select('tbody', true).first().dom.innerHTML = '';
6641 * Show or hide a row.
6642 * @param {Number} rowIndex to show or hide
6643 * @param {Boolean} state hide
6645 setRowVisibility : function(rowIndex, state)
6647 var bt = this.mainBody.dom;
6649 var rows = this.el.select('tbody > tr', true).elements;
6651 if(typeof(rows[rowIndex]) == 'undefined'){
6654 rows[rowIndex].dom.style.display = state ? '' : 'none';
6658 getSelectionModel : function(){
6660 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6662 return this.selModel;
6665 * Render the Roo.bootstrap object from renderder
6667 renderCellObject : function(r)
6671 var t = r.cfg.render(r.container);
6674 Roo.each(r.cfg.cn, function(c){
6676 container: t.getChildContainer(),
6679 _this.renderCellObject(child);
6684 getRowIndex : function(row)
6688 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6699 * Returns the grid's underlying element = used by panel.Grid
6700 * @return {Element} The element
6702 getGridEl : function(){
6706 * Forces a resize - used by panel.Grid
6707 * @return {Element} The element
6709 autoSize : function()
6711 //var ctr = Roo.get(this.container.dom.parentElement);
6712 var ctr = Roo.get(this.el.dom);
6714 var thd = this.getGridEl().select('thead',true).first();
6715 var tbd = this.getGridEl().select('tbody', true).first();
6718 var cw = ctr.getWidth();
6722 tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6723 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6726 cw = Math.max(cw, this.totalWidth);
6727 this.getGridEl().select('tr',true).setWidth(cw);
6728 // resize 'expandable coloumn?
6730 return; // we doe not have a view in this design..
6733 onBodyScroll: function()
6736 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6737 this.mainHead.setStyle({
6738 'position' : 'relative',
6739 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6756 * @class Roo.bootstrap.TableCell
6757 * @extends Roo.bootstrap.Component
6758 * Bootstrap TableCell class
6759 * @cfg {String} html cell contain text
6760 * @cfg {String} cls cell class
6761 * @cfg {String} tag cell tag (td|th) default td
6762 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6763 * @cfg {String} align Aligns the content in a cell
6764 * @cfg {String} axis Categorizes cells
6765 * @cfg {String} bgcolor Specifies the background color of a cell
6766 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6767 * @cfg {Number} colspan Specifies the number of columns a cell should span
6768 * @cfg {String} headers Specifies one or more header cells a cell is related to
6769 * @cfg {Number} height Sets the height of a cell
6770 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6771 * @cfg {Number} rowspan Sets the number of rows a cell should span
6772 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6773 * @cfg {String} valign Vertical aligns the content in a cell
6774 * @cfg {Number} width Specifies the width of a cell
6777 * Create a new TableCell
6778 * @param {Object} config The config object
6781 Roo.bootstrap.TableCell = function(config){
6782 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6785 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6805 getAutoCreate : function(){
6806 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6826 cfg.align=this.align
6832 cfg.bgcolor=this.bgcolor
6835 cfg.charoff=this.charoff
6838 cfg.colspan=this.colspan
6841 cfg.headers=this.headers
6844 cfg.height=this.height
6847 cfg.nowrap=this.nowrap
6850 cfg.rowspan=this.rowspan
6853 cfg.scope=this.scope
6856 cfg.valign=this.valign
6859 cfg.width=this.width
6878 * @class Roo.bootstrap.TableRow
6879 * @extends Roo.bootstrap.Component
6880 * Bootstrap TableRow class
6881 * @cfg {String} cls row class
6882 * @cfg {String} align Aligns the content in a table row
6883 * @cfg {String} bgcolor Specifies a background color for a table row
6884 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6885 * @cfg {String} valign Vertical aligns the content in a table row
6888 * Create a new TableRow
6889 * @param {Object} config The config object
6892 Roo.bootstrap.TableRow = function(config){
6893 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6896 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6904 getAutoCreate : function(){
6905 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6915 cfg.align = this.align;
6918 cfg.bgcolor = this.bgcolor;
6921 cfg.charoff = this.charoff;
6924 cfg.valign = this.valign;
6942 * @class Roo.bootstrap.TableBody
6943 * @extends Roo.bootstrap.Component
6944 * Bootstrap TableBody class
6945 * @cfg {String} cls element class
6946 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6947 * @cfg {String} align Aligns the content inside the element
6948 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6949 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6952 * Create a new TableBody
6953 * @param {Object} config The config object
6956 Roo.bootstrap.TableBody = function(config){
6957 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6960 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6968 getAutoCreate : function(){
6969 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6983 cfg.align = this.align;
6986 cfg.charoff = this.charoff;
6989 cfg.valign = this.valign;
6996 // initEvents : function()
7003 // this.store = Roo.factory(this.store, Roo.data);
7004 // this.store.on('load', this.onLoad, this);
7006 // this.store.load();
7010 // onLoad: function ()
7012 // this.fireEvent('load', this);
7022 * Ext JS Library 1.1.1
7023 * Copyright(c) 2006-2007, Ext JS, LLC.
7025 * Originally Released Under LGPL - original licence link has changed is not relivant.
7028 * <script type="text/javascript">
7031 // as we use this in bootstrap.
7032 Roo.namespace('Roo.form');
7034 * @class Roo.form.Action
7035 * Internal Class used to handle form actions
7037 * @param {Roo.form.BasicForm} el The form element or its id
7038 * @param {Object} config Configuration options
7043 // define the action interface
7044 Roo.form.Action = function(form, options){
7046 this.options = options || {};
7049 * Client Validation Failed
7052 Roo.form.Action.CLIENT_INVALID = 'client';
7054 * Server Validation Failed
7057 Roo.form.Action.SERVER_INVALID = 'server';
7059 * Connect to Server Failed
7062 Roo.form.Action.CONNECT_FAILURE = 'connect';
7064 * Reading Data from Server Failed
7067 Roo.form.Action.LOAD_FAILURE = 'load';
7069 Roo.form.Action.prototype = {
7071 failureType : undefined,
7072 response : undefined,
7076 run : function(options){
7081 success : function(response){
7086 handleResponse : function(response){
7090 // default connection failure
7091 failure : function(response){
7093 this.response = response;
7094 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7095 this.form.afterAction(this, false);
7098 processResponse : function(response){
7099 this.response = response;
7100 if(!response.responseText){
7103 this.result = this.handleResponse(response);
7107 // utility functions used internally
7108 getUrl : function(appendParams){
7109 var url = this.options.url || this.form.url || this.form.el.dom.action;
7111 var p = this.getParams();
7113 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7119 getMethod : function(){
7120 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7123 getParams : function(){
7124 var bp = this.form.baseParams;
7125 var p = this.options.params;
7127 if(typeof p == "object"){
7128 p = Roo.urlEncode(Roo.applyIf(p, bp));
7129 }else if(typeof p == 'string' && bp){
7130 p += '&' + Roo.urlEncode(bp);
7133 p = Roo.urlEncode(bp);
7138 createCallback : function(){
7140 success: this.success,
7141 failure: this.failure,
7143 timeout: (this.form.timeout*1000),
7144 upload: this.form.fileUpload ? this.success : undefined
7149 Roo.form.Action.Submit = function(form, options){
7150 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7153 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7156 haveProgress : false,
7157 uploadComplete : false,
7159 // uploadProgress indicator.
7160 uploadProgress : function()
7162 if (!this.form.progressUrl) {
7166 if (!this.haveProgress) {
7167 Roo.MessageBox.progress("Uploading", "Uploading");
7169 if (this.uploadComplete) {
7170 Roo.MessageBox.hide();
7174 this.haveProgress = true;
7176 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7178 var c = new Roo.data.Connection();
7180 url : this.form.progressUrl,
7185 success : function(req){
7186 //console.log(data);
7190 rdata = Roo.decode(req.responseText)
7192 Roo.log("Invalid data from server..");
7196 if (!rdata || !rdata.success) {
7198 Roo.MessageBox.alert(Roo.encode(rdata));
7201 var data = rdata.data;
7203 if (this.uploadComplete) {
7204 Roo.MessageBox.hide();
7209 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7210 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7213 this.uploadProgress.defer(2000,this);
7216 failure: function(data) {
7217 Roo.log('progress url failed ');
7228 // run get Values on the form, so it syncs any secondary forms.
7229 this.form.getValues();
7231 var o = this.options;
7232 var method = this.getMethod();
7233 var isPost = method == 'POST';
7234 if(o.clientValidation === false || this.form.isValid()){
7236 if (this.form.progressUrl) {
7237 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7238 (new Date() * 1) + '' + Math.random());
7243 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7244 form:this.form.el.dom,
7245 url:this.getUrl(!isPost),
7247 params:isPost ? this.getParams() : null,
7248 isUpload: this.form.fileUpload
7251 this.uploadProgress();
7253 }else if (o.clientValidation !== false){ // client validation failed
7254 this.failureType = Roo.form.Action.CLIENT_INVALID;
7255 this.form.afterAction(this, false);
7259 success : function(response)
7261 this.uploadComplete= true;
7262 if (this.haveProgress) {
7263 Roo.MessageBox.hide();
7267 var result = this.processResponse(response);
7268 if(result === true || result.success){
7269 this.form.afterAction(this, true);
7273 this.form.markInvalid(result.errors);
7274 this.failureType = Roo.form.Action.SERVER_INVALID;
7276 this.form.afterAction(this, false);
7278 failure : function(response)
7280 this.uploadComplete= true;
7281 if (this.haveProgress) {
7282 Roo.MessageBox.hide();
7285 this.response = response;
7286 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7287 this.form.afterAction(this, false);
7290 handleResponse : function(response){
7291 if(this.form.errorReader){
7292 var rs = this.form.errorReader.read(response);
7295 for(var i = 0, len = rs.records.length; i < len; i++) {
7296 var r = rs.records[i];
7300 if(errors.length < 1){
7304 success : rs.success,
7310 ret = Roo.decode(response.responseText);
7314 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7324 Roo.form.Action.Load = function(form, options){
7325 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7326 this.reader = this.form.reader;
7329 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7334 Roo.Ajax.request(Roo.apply(
7335 this.createCallback(), {
7336 method:this.getMethod(),
7337 url:this.getUrl(false),
7338 params:this.getParams()
7342 success : function(response){
7344 var result = this.processResponse(response);
7345 if(result === true || !result.success || !result.data){
7346 this.failureType = Roo.form.Action.LOAD_FAILURE;
7347 this.form.afterAction(this, false);
7350 this.form.clearInvalid();
7351 this.form.setValues(result.data);
7352 this.form.afterAction(this, true);
7355 handleResponse : function(response){
7356 if(this.form.reader){
7357 var rs = this.form.reader.read(response);
7358 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7360 success : rs.success,
7364 return Roo.decode(response.responseText);
7368 Roo.form.Action.ACTION_TYPES = {
7369 'load' : Roo.form.Action.Load,
7370 'submit' : Roo.form.Action.Submit
7379 * @class Roo.bootstrap.Form
7380 * @extends Roo.bootstrap.Component
7381 * Bootstrap Form class
7382 * @cfg {String} method GET | POST (default POST)
7383 * @cfg {String} labelAlign top | left (default top)
7384 * @cfg {String} align left | right - for navbars
7385 * @cfg {Boolean} loadMask load mask when submit (default true)
7390 * @param {Object} config The config object
7394 Roo.bootstrap.Form = function(config){
7395 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7398 * @event clientvalidation
7399 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7400 * @param {Form} this
7401 * @param {Boolean} valid true if the form has passed client-side validation
7403 clientvalidation: true,
7405 * @event beforeaction
7406 * Fires before any action is performed. Return false to cancel the action.
7407 * @param {Form} this
7408 * @param {Action} action The action to be performed
7412 * @event actionfailed
7413 * Fires when an action fails.
7414 * @param {Form} this
7415 * @param {Action} action The action that failed
7417 actionfailed : true,
7419 * @event actioncomplete
7420 * Fires when an action is completed.
7421 * @param {Form} this
7422 * @param {Action} action The action that completed
7424 actioncomplete : true
7429 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7432 * @cfg {String} method
7433 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7438 * The URL to use for form actions if one isn't supplied in the action options.
7441 * @cfg {Boolean} fileUpload
7442 * Set to true if this form is a file upload.
7446 * @cfg {Object} baseParams
7447 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7451 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7455 * @cfg {Sting} align (left|right) for navbar forms
7460 activeAction : null,
7463 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7464 * element by passing it or its id or mask the form itself by passing in true.
7467 waitMsgTarget : false,
7471 getAutoCreate : function(){
7475 method : this.method || 'POST',
7476 id : this.id || Roo.id(),
7479 if (this.parent().xtype.match(/^Nav/)) {
7480 cfg.cls = 'navbar-form navbar-' + this.align;
7484 if (this.labelAlign == 'left' ) {
7485 cfg.cls += ' form-horizontal';
7491 initEvents : function()
7493 this.el.on('submit', this.onSubmit, this);
7494 // this was added as random key presses on the form where triggering form submit.
7495 this.el.on('keypress', function(e) {
7496 if (e.getCharCode() != 13) {
7499 // we might need to allow it for textareas.. and some other items.
7500 // check e.getTarget().
7502 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7506 Roo.log("keypress blocked");
7514 onSubmit : function(e){
7519 * Returns true if client-side validation on the form is successful.
7522 isValid : function(){
7523 var items = this.getItems();
7525 items.each(function(f){
7534 * Returns true if any fields in this form have changed since their original load.
7537 isDirty : function(){
7539 var items = this.getItems();
7540 items.each(function(f){
7550 * Performs a predefined action (submit or load) or custom actions you define on this form.
7551 * @param {String} actionName The name of the action type
7552 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7553 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7554 * accept other config options):
7556 Property Type Description
7557 ---------------- --------------- ----------------------------------------------------------------------------------
7558 url String The url for the action (defaults to the form's url)
7559 method String The form method to use (defaults to the form's method, or POST if not defined)
7560 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7561 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7562 validate the form on the client (defaults to false)
7564 * @return {BasicForm} this
7566 doAction : function(action, options){
7567 if(typeof action == 'string'){
7568 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7570 if(this.fireEvent('beforeaction', this, action) !== false){
7571 this.beforeAction(action);
7572 action.run.defer(100, action);
7578 beforeAction : function(action){
7579 var o = action.options;
7582 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7584 // not really supported yet.. ??
7586 //if(this.waitMsgTarget === true){
7587 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7588 //}else if(this.waitMsgTarget){
7589 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7590 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7592 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7598 afterAction : function(action, success){
7599 this.activeAction = null;
7600 var o = action.options;
7602 //if(this.waitMsgTarget === true){
7604 //}else if(this.waitMsgTarget){
7605 // this.waitMsgTarget.unmask();
7607 // Roo.MessageBox.updateProgress(1);
7608 // Roo.MessageBox.hide();
7615 Roo.callback(o.success, o.scope, [this, action]);
7616 this.fireEvent('actioncomplete', this, action);
7620 // failure condition..
7621 // we have a scenario where updates need confirming.
7622 // eg. if a locking scenario exists..
7623 // we look for { errors : { needs_confirm : true }} in the response.
7625 (typeof(action.result) != 'undefined') &&
7626 (typeof(action.result.errors) != 'undefined') &&
7627 (typeof(action.result.errors.needs_confirm) != 'undefined')
7630 Roo.log("not supported yet");
7633 Roo.MessageBox.confirm(
7634 "Change requires confirmation",
7635 action.result.errorMsg,
7640 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7650 Roo.callback(o.failure, o.scope, [this, action]);
7651 // show an error message if no failed handler is set..
7652 if (!this.hasListener('actionfailed')) {
7653 Roo.log("need to add dialog support");
7655 Roo.MessageBox.alert("Error",
7656 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7657 action.result.errorMsg :
7658 "Saving Failed, please check your entries or try again"
7663 this.fireEvent('actionfailed', this, action);
7668 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7669 * @param {String} id The value to search for
7672 findField : function(id){
7673 var items = this.getItems();
7674 var field = items.get(id);
7676 items.each(function(f){
7677 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7684 return field || null;
7687 * Mark fields in this form invalid in bulk.
7688 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7689 * @return {BasicForm} this
7691 markInvalid : function(errors){
7692 if(errors instanceof Array){
7693 for(var i = 0, len = errors.length; i < len; i++){
7694 var fieldError = errors[i];
7695 var f = this.findField(fieldError.id);
7697 f.markInvalid(fieldError.msg);
7703 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7704 field.markInvalid(errors[id]);
7708 //Roo.each(this.childForms || [], function (f) {
7709 // f.markInvalid(errors);
7716 * Set values for fields in this form in bulk.
7717 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7718 * @return {BasicForm} this
7720 setValues : function(values){
7721 if(values instanceof Array){ // array of objects
7722 for(var i = 0, len = values.length; i < len; i++){
7724 var f = this.findField(v.id);
7726 f.setValue(v.value);
7727 if(this.trackResetOnLoad){
7728 f.originalValue = f.getValue();
7732 }else{ // object hash
7735 if(typeof values[id] != 'function' && (field = this.findField(id))){
7737 if (field.setFromData &&
7739 field.displayField &&
7740 // combos' with local stores can
7741 // be queried via setValue()
7742 // to set their value..
7743 (field.store && !field.store.isLocal)
7747 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7748 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7749 field.setFromData(sd);
7752 field.setValue(values[id]);
7756 if(this.trackResetOnLoad){
7757 field.originalValue = field.getValue();
7763 //Roo.each(this.childForms || [], function (f) {
7764 // f.setValues(values);
7771 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7772 * they are returned as an array.
7773 * @param {Boolean} asString
7776 getValues : function(asString){
7777 //if (this.childForms) {
7778 // copy values from the child forms
7779 // Roo.each(this.childForms, function (f) {
7780 // this.setValues(f.getValues());
7786 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7787 if(asString === true){
7790 return Roo.urlDecode(fs);
7794 * Returns the fields in this form as an object with key/value pairs.
7795 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7798 getFieldValues : function(with_hidden)
7800 var items = this.getItems();
7802 items.each(function(f){
7806 var v = f.getValue();
7807 if (f.inputType =='radio') {
7808 if (typeof(ret[f.getName()]) == 'undefined') {
7809 ret[f.getName()] = ''; // empty..
7812 if (!f.el.dom.checked) {
7820 // not sure if this supported any more..
7821 if ((typeof(v) == 'object') && f.getRawValue) {
7822 v = f.getRawValue() ; // dates..
7824 // combo boxes where name != hiddenName...
7825 if (f.name != f.getName()) {
7826 ret[f.name] = f.getRawValue();
7828 ret[f.getName()] = v;
7835 * Clears all invalid messages in this form.
7836 * @return {BasicForm} this
7838 clearInvalid : function(){
7839 var items = this.getItems();
7841 items.each(function(f){
7852 * @return {BasicForm} this
7855 var items = this.getItems();
7856 items.each(function(f){
7860 Roo.each(this.childForms || [], function (f) {
7867 getItems : function()
7869 var r=new Roo.util.MixedCollection(false, function(o){
7870 return o.id || (o.id = Roo.id());
7872 var iter = function(el) {
7879 Roo.each(el.items,function(e) {
7899 * Ext JS Library 1.1.1
7900 * Copyright(c) 2006-2007, Ext JS, LLC.
7902 * Originally Released Under LGPL - original licence link has changed is not relivant.
7905 * <script type="text/javascript">
7908 * @class Roo.form.VTypes
7909 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7912 Roo.form.VTypes = function(){
7913 // closure these in so they are only created once.
7914 var alpha = /^[a-zA-Z_]+$/;
7915 var alphanum = /^[a-zA-Z0-9_]+$/;
7916 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7917 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7919 // All these messages and functions are configurable
7922 * The function used to validate email addresses
7923 * @param {String} value The email address
7925 'email' : function(v){
7926 return email.test(v);
7929 * The error text to display when the email validation function returns false
7932 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7934 * The keystroke filter mask to be applied on email input
7937 'emailMask' : /[a-z0-9_\.\-@]/i,
7940 * The function used to validate URLs
7941 * @param {String} value The URL
7943 'url' : function(v){
7947 * The error text to display when the url validation function returns false
7950 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7953 * The function used to validate alpha values
7954 * @param {String} value The value
7956 'alpha' : function(v){
7957 return alpha.test(v);
7960 * The error text to display when the alpha validation function returns false
7963 'alphaText' : 'This field should only contain letters and _',
7965 * The keystroke filter mask to be applied on alpha input
7968 'alphaMask' : /[a-z_]/i,
7971 * The function used to validate alphanumeric values
7972 * @param {String} value The value
7974 'alphanum' : function(v){
7975 return alphanum.test(v);
7978 * The error text to display when the alphanumeric validation function returns false
7981 'alphanumText' : 'This field should only contain letters, numbers and _',
7983 * The keystroke filter mask to be applied on alphanumeric input
7986 'alphanumMask' : /[a-z0-9_]/i
7996 * @class Roo.bootstrap.Input
7997 * @extends Roo.bootstrap.Component
7998 * Bootstrap Input class
7999 * @cfg {Boolean} disabled is it disabled
8000 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8001 * @cfg {String} name name of the input
8002 * @cfg {string} fieldLabel - the label associated
8003 * @cfg {string} placeholder - placeholder to put in text.
8004 * @cfg {string} before - input group add on before
8005 * @cfg {string} after - input group add on after
8006 * @cfg {string} size - (lg|sm) or leave empty..
8007 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8008 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8009 * @cfg {Number} md colspan out of 12 for computer-sized screens
8010 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8011 * @cfg {string} value default value of the input
8012 * @cfg {Number} labelWidth set the width of label (0-12)
8013 * @cfg {String} labelAlign (top|left)
8014 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8015 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8016 * @cfg {String} indicatorpos (left|right) default left
8018 * @cfg {String} align (left|center|right) Default left
8019 * @cfg {Boolean} forceFeedback (true|false) Default false
8025 * Create a new Input
8026 * @param {Object} config The config object
8029 Roo.bootstrap.Input = function(config){
8030 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8035 * Fires when this field receives input focus.
8036 * @param {Roo.form.Field} this
8041 * Fires when this field loses input focus.
8042 * @param {Roo.form.Field} this
8047 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8048 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8049 * @param {Roo.form.Field} this
8050 * @param {Roo.EventObject} e The event object
8055 * Fires just before the field blurs if the field value has changed.
8056 * @param {Roo.form.Field} this
8057 * @param {Mixed} newValue The new value
8058 * @param {Mixed} oldValue The original value
8063 * Fires after the field has been marked as invalid.
8064 * @param {Roo.form.Field} this
8065 * @param {String} msg The validation message
8070 * Fires after the field has been validated with no errors.
8071 * @param {Roo.form.Field} this
8076 * Fires after the key up
8077 * @param {Roo.form.Field} this
8078 * @param {Roo.EventObject} e The event Object
8084 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8086 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8087 automatic validation (defaults to "keyup").
8089 validationEvent : "keyup",
8091 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8093 validateOnBlur : true,
8095 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8097 validationDelay : 250,
8099 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8101 focusClass : "x-form-focus", // not needed???
8105 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8107 invalidClass : "has-warning",
8110 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8112 validClass : "has-success",
8115 * @cfg {Boolean} hasFeedback (true|false) default true
8120 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8122 invalidFeedbackClass : "glyphicon-warning-sign",
8125 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8127 validFeedbackClass : "glyphicon-ok",
8130 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8132 selectOnFocus : false,
8135 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8139 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8144 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8146 disableKeyFilter : false,
8149 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8153 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8157 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8159 blankText : "This field is required",
8162 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8166 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8168 maxLength : Number.MAX_VALUE,
8170 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8172 minLengthText : "The minimum length for this field is {0}",
8174 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8176 maxLengthText : "The maximum length for this field is {0}",
8180 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8181 * If available, this function will be called only after the basic validators all return true, and will be passed the
8182 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8186 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8187 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8188 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8192 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8196 autocomplete: false,
8215 formatedValue : false,
8216 forceFeedback : false,
8218 indicatorpos : 'left',
8220 parentLabelAlign : function()
8223 while (parent.parent()) {
8224 parent = parent.parent();
8225 if (typeof(parent.labelAlign) !='undefined') {
8226 return parent.labelAlign;
8233 getAutoCreate : function()
8235 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8241 if(this.inputType != 'hidden'){
8242 cfg.cls = 'form-group' //input-group
8248 type : this.inputType,
8250 cls : 'form-control',
8251 placeholder : this.placeholder || '',
8252 autocomplete : this.autocomplete || 'new-password'
8256 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8259 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8260 input.maxLength = this.maxLength;
8263 if (this.disabled) {
8264 input.disabled=true;
8267 if (this.readOnly) {
8268 input.readonly=true;
8272 input.name = this.name;
8276 input.cls += ' input-' + this.size;
8280 ['xs','sm','md','lg'].map(function(size){
8281 if (settings[size]) {
8282 cfg.cls += ' col-' + size + '-' + settings[size];
8286 var inputblock = input;
8290 cls: 'glyphicon form-control-feedback'
8293 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8296 cls : 'has-feedback',
8304 if (this.before || this.after) {
8307 cls : 'input-group',
8311 if (this.before && typeof(this.before) == 'string') {
8313 inputblock.cn.push({
8315 cls : 'roo-input-before input-group-addon',
8319 if (this.before && typeof(this.before) == 'object') {
8320 this.before = Roo.factory(this.before);
8322 inputblock.cn.push({
8324 cls : 'roo-input-before input-group-' +
8325 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8329 inputblock.cn.push(input);
8331 if (this.after && typeof(this.after) == 'string') {
8332 inputblock.cn.push({
8334 cls : 'roo-input-after input-group-addon',
8338 if (this.after && typeof(this.after) == 'object') {
8339 this.after = Roo.factory(this.after);
8341 inputblock.cn.push({
8343 cls : 'roo-input-after input-group-' +
8344 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8348 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8349 inputblock.cls += ' has-feedback';
8350 inputblock.cn.push(feedback);
8354 if (align ==='left' && this.fieldLabel.length) {
8359 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8360 tooltip : 'This field is required'
8365 cls : 'control-label col-sm-' + this.labelWidth,
8366 html : this.fieldLabel
8370 cls : "col-sm-" + (12 - this.labelWidth),
8378 if(this.indicatorpos == 'right'){
8383 cls : 'control-label col-sm-' + this.labelWidth,
8384 html : this.fieldLabel
8389 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8390 tooltip : 'This field is required'
8393 cls : "col-sm-" + (12 - this.labelWidth),
8402 } else if ( this.fieldLabel.length) {
8407 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8408 tooltip : 'This field is required'
8412 //cls : 'input-group-addon',
8413 html : this.fieldLabel
8421 if(this.indicatorpos == 'right'){
8426 //cls : 'input-group-addon',
8427 html : this.fieldLabel
8432 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8433 tooltip : 'This field is required'
8453 if (this.parentType === 'Navbar' && this.parent().bar) {
8454 cfg.cls += ' navbar-form';
8457 if (this.parentType === 'NavGroup') {
8458 cfg.cls += ' navbar-form';
8466 * return the real input element.
8468 inputEl: function ()
8470 return this.el.select('input.form-control',true).first();
8473 tooltipEl : function()
8475 return this.inputEl();
8478 indicatorEl : function()
8480 var indicator = this.el.select('i.roo-required-indicator',true).first();
8490 setDisabled : function(v)
8492 var i = this.inputEl().dom;
8494 i.removeAttribute('disabled');
8498 i.setAttribute('disabled','true');
8500 initEvents : function()
8503 this.inputEl().on("keydown" , this.fireKey, this);
8504 this.inputEl().on("focus", this.onFocus, this);
8505 this.inputEl().on("blur", this.onBlur, this);
8507 this.inputEl().relayEvent('keyup', this);
8509 this.indicator = this.indicatorEl();
8512 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8513 this.indicator.hide();
8516 // reference to original value for reset
8517 this.originalValue = this.getValue();
8518 //Roo.form.TextField.superclass.initEvents.call(this);
8519 if(this.validationEvent == 'keyup'){
8520 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8521 this.inputEl().on('keyup', this.filterValidation, this);
8523 else if(this.validationEvent !== false){
8524 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8527 if(this.selectOnFocus){
8528 this.on("focus", this.preFocus, this);
8531 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8532 this.inputEl().on("keypress", this.filterKeys, this);
8535 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8536 this.el.on("click", this.autoSize, this);
8539 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8540 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8543 if (typeof(this.before) == 'object') {
8544 this.before.render(this.el.select('.roo-input-before',true).first());
8546 if (typeof(this.after) == 'object') {
8547 this.after.render(this.el.select('.roo-input-after',true).first());
8552 filterValidation : function(e){
8553 if(!e.isNavKeyPress()){
8554 this.validationTask.delay(this.validationDelay);
8558 * Validates the field value
8559 * @return {Boolean} True if the value is valid, else false
8561 validate : function(){
8562 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8563 if(this.disabled || this.validateValue(this.getRawValue())){
8574 * Validates a value according to the field's validation rules and marks the field as invalid
8575 * if the validation fails
8576 * @param {Mixed} value The value to validate
8577 * @return {Boolean} True if the value is valid, else false
8579 validateValue : function(value){
8580 if(value.length < 1) { // if it's blank
8581 if(this.allowBlank){
8587 if(value.length < this.minLength){
8590 if(value.length > this.maxLength){
8594 var vt = Roo.form.VTypes;
8595 if(!vt[this.vtype](value, this)){
8599 if(typeof this.validator == "function"){
8600 var msg = this.validator(value);
8606 if(this.regex && !this.regex.test(value)){
8616 fireKey : function(e){
8617 //Roo.log('field ' + e.getKey());
8618 if(e.isNavKeyPress()){
8619 this.fireEvent("specialkey", this, e);
8622 focus : function (selectText){
8624 this.inputEl().focus();
8625 if(selectText === true){
8626 this.inputEl().dom.select();
8632 onFocus : function(){
8633 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8634 // this.el.addClass(this.focusClass);
8637 this.hasFocus = true;
8638 this.startValue = this.getValue();
8639 this.fireEvent("focus", this);
8643 beforeBlur : Roo.emptyFn,
8647 onBlur : function(){
8649 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8650 //this.el.removeClass(this.focusClass);
8652 this.hasFocus = false;
8653 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8656 var v = this.getValue();
8657 if(String(v) !== String(this.startValue)){
8658 this.fireEvent('change', this, v, this.startValue);
8660 this.fireEvent("blur", this);
8664 * Resets the current field value to the originally loaded value and clears any validation messages
8667 this.setValue(this.originalValue);
8671 * Returns the name of the field
8672 * @return {Mixed} name The name field
8674 getName: function(){
8678 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8679 * @return {Mixed} value The field value
8681 getValue : function(){
8683 var v = this.inputEl().getValue();
8688 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8689 * @return {Mixed} value The field value
8691 getRawValue : function(){
8692 var v = this.inputEl().getValue();
8698 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8699 * @param {Mixed} value The value to set
8701 setRawValue : function(v){
8702 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8705 selectText : function(start, end){
8706 var v = this.getRawValue();
8708 start = start === undefined ? 0 : start;
8709 end = end === undefined ? v.length : end;
8710 var d = this.inputEl().dom;
8711 if(d.setSelectionRange){
8712 d.setSelectionRange(start, end);
8713 }else if(d.createTextRange){
8714 var range = d.createTextRange();
8715 range.moveStart("character", start);
8716 range.moveEnd("character", v.length-end);
8723 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8724 * @param {Mixed} value The value to set
8726 setValue : function(v){
8729 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8735 processValue : function(value){
8736 if(this.stripCharsRe){
8737 var newValue = value.replace(this.stripCharsRe, '');
8738 if(newValue !== value){
8739 this.setRawValue(newValue);
8746 preFocus : function(){
8748 if(this.selectOnFocus){
8749 this.inputEl().dom.select();
8752 filterKeys : function(e){
8754 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8757 var c = e.getCharCode(), cc = String.fromCharCode(c);
8758 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8761 if(!this.maskRe.test(cc)){
8766 * Clear any invalid styles/messages for this field
8768 clearInvalid : function(){
8770 if(!this.el || this.preventMark){ // not rendered
8775 this.indicator.hide();
8778 this.el.removeClass(this.invalidClass);
8780 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8782 var feedback = this.el.select('.form-control-feedback', true).first();
8785 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8790 this.fireEvent('valid', this);
8794 * Mark this field as valid
8796 markValid : function()
8798 if(!this.el || this.preventMark){ // not rendered
8802 this.el.removeClass([this.invalidClass, this.validClass]);
8804 var feedback = this.el.select('.form-control-feedback', true).first();
8807 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8810 if(this.disabled || this.allowBlank){
8815 this.indicator.hide();
8818 this.el.addClass(this.validClass);
8820 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8822 var feedback = this.el.select('.form-control-feedback', true).first();
8825 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8826 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8831 this.fireEvent('valid', this);
8835 * Mark this field as invalid
8836 * @param {String} msg The validation message
8838 markInvalid : function(msg)
8840 if(!this.el || this.preventMark){ // not rendered
8844 this.el.removeClass([this.invalidClass, this.validClass]);
8846 var feedback = this.el.select('.form-control-feedback', true).first();
8849 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8852 if(this.disabled || this.allowBlank){
8857 this.indicator.show();
8860 this.el.addClass(this.invalidClass);
8862 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8864 var feedback = this.el.select('.form-control-feedback', true).first();
8867 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8869 if(this.getValue().length || this.forceFeedback){
8870 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8877 this.fireEvent('invalid', this, msg);
8880 SafariOnKeyDown : function(event)
8882 // this is a workaround for a password hang bug on chrome/ webkit.
8884 var isSelectAll = false;
8886 if(this.inputEl().dom.selectionEnd > 0){
8887 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8889 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8890 event.preventDefault();
8895 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8897 event.preventDefault();
8898 // this is very hacky as keydown always get's upper case.
8900 var cc = String.fromCharCode(event.getCharCode());
8901 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8905 adjustWidth : function(tag, w){
8906 tag = tag.toLowerCase();
8907 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8908 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8912 if(tag == 'textarea'){
8915 }else if(Roo.isOpera){
8919 if(tag == 'textarea'){
8938 * @class Roo.bootstrap.TextArea
8939 * @extends Roo.bootstrap.Input
8940 * Bootstrap TextArea class
8941 * @cfg {Number} cols Specifies the visible width of a text area
8942 * @cfg {Number} rows Specifies the visible number of lines in a text area
8943 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8944 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8945 * @cfg {string} html text
8948 * Create a new TextArea
8949 * @param {Object} config The config object
8952 Roo.bootstrap.TextArea = function(config){
8953 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8957 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8967 getAutoCreate : function(){
8969 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8980 value : this.value || '',
8981 html: this.html || '',
8982 cls : 'form-control',
8983 placeholder : this.placeholder || ''
8987 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8988 input.maxLength = this.maxLength;
8992 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8996 input.cols = this.cols;
8999 if (this.readOnly) {
9000 input.readonly = true;
9004 input.name = this.name;
9008 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9012 ['xs','sm','md','lg'].map(function(size){
9013 if (settings[size]) {
9014 cfg.cls += ' col-' + size + '-' + settings[size];
9018 var inputblock = input;
9020 if(this.hasFeedback && !this.allowBlank){
9024 cls: 'glyphicon form-control-feedback'
9028 cls : 'has-feedback',
9037 if (this.before || this.after) {
9040 cls : 'input-group',
9044 inputblock.cn.push({
9046 cls : 'input-group-addon',
9051 inputblock.cn.push(input);
9053 if(this.hasFeedback && !this.allowBlank){
9054 inputblock.cls += ' has-feedback';
9055 inputblock.cn.push(feedback);
9059 inputblock.cn.push({
9061 cls : 'input-group-addon',
9068 if (align ==='left' && this.fieldLabel.length) {
9069 // Roo.log("left and has label");
9075 cls : 'control-label col-sm-' + this.labelWidth,
9076 html : this.fieldLabel
9080 cls : "col-sm-" + (12 - this.labelWidth),
9087 } else if ( this.fieldLabel.length) {
9088 // Roo.log(" label");
9093 //cls : 'input-group-addon',
9094 html : this.fieldLabel
9104 // Roo.log(" no label && no align");
9114 if (this.disabled) {
9115 input.disabled=true;
9122 * return the real textarea element.
9124 inputEl: function ()
9126 return this.el.select('textarea.form-control',true).first();
9130 * Clear any invalid styles/messages for this field
9132 clearInvalid : function()
9135 if(!this.el || this.preventMark){ // not rendered
9139 var label = this.el.select('label', true).first();
9140 var icon = this.el.select('i.fa-star', true).first();
9146 this.el.removeClass(this.invalidClass);
9148 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9150 var feedback = this.el.select('.form-control-feedback', true).first();
9153 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9158 this.fireEvent('valid', this);
9162 * Mark this field as valid
9164 markValid : function()
9166 if(!this.el || this.preventMark){ // not rendered
9170 this.el.removeClass([this.invalidClass, this.validClass]);
9172 var feedback = this.el.select('.form-control-feedback', true).first();
9175 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9178 if(this.disabled || this.allowBlank){
9182 var label = this.el.select('label', true).first();
9183 var icon = this.el.select('i.fa-star', true).first();
9189 this.el.addClass(this.validClass);
9191 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9193 var feedback = this.el.select('.form-control-feedback', true).first();
9196 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9197 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9202 this.fireEvent('valid', this);
9206 * Mark this field as invalid
9207 * @param {String} msg The validation message
9209 markInvalid : function(msg)
9211 if(!this.el || this.preventMark){ // not rendered
9215 this.el.removeClass([this.invalidClass, this.validClass]);
9217 var feedback = this.el.select('.form-control-feedback', true).first();
9220 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9223 if(this.disabled || this.allowBlank){
9227 var label = this.el.select('label', true).first();
9228 var icon = this.el.select('i.fa-star', true).first();
9230 if(!this.getValue().length && label && !icon){
9231 this.el.createChild({
9233 cls : 'text-danger fa fa-lg fa-star',
9234 tooltip : 'This field is required',
9235 style : 'margin-right:5px;'
9239 this.el.addClass(this.invalidClass);
9241 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9243 var feedback = this.el.select('.form-control-feedback', true).first();
9246 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9248 if(this.getValue().length || this.forceFeedback){
9249 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9256 this.fireEvent('invalid', this, msg);
9264 * trigger field - base class for combo..
9269 * @class Roo.bootstrap.TriggerField
9270 * @extends Roo.bootstrap.Input
9271 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9272 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9273 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9274 * for which you can provide a custom implementation. For example:
9276 var trigger = new Roo.bootstrap.TriggerField();
9277 trigger.onTriggerClick = myTriggerFn;
9278 trigger.applyTo('my-field');
9281 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9282 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9283 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9284 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9285 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9288 * Create a new TriggerField.
9289 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9290 * to the base TextField)
9292 Roo.bootstrap.TriggerField = function(config){
9293 this.mimicing = false;
9294 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9297 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9299 * @cfg {String} triggerClass A CSS class to apply to the trigger
9302 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9307 * @cfg {Boolean} removable (true|false) special filter default false
9311 /** @cfg {Boolean} grow @hide */
9312 /** @cfg {Number} growMin @hide */
9313 /** @cfg {Number} growMax @hide */
9319 autoSize: Roo.emptyFn,
9326 actionMode : 'wrap',
9331 getAutoCreate : function(){
9333 var align = this.labelAlign || this.parentLabelAlign();
9338 cls: 'form-group' //input-group
9345 type : this.inputType,
9346 cls : 'form-control',
9347 autocomplete: 'new-password',
9348 placeholder : this.placeholder || ''
9352 input.name = this.name;
9355 input.cls += ' input-' + this.size;
9358 if (this.disabled) {
9359 input.disabled=true;
9362 var inputblock = input;
9364 if(this.hasFeedback && !this.allowBlank){
9368 cls: 'glyphicon form-control-feedback'
9371 if(this.removable && !this.editable && !this.tickable){
9373 cls : 'has-feedback',
9379 cls : 'roo-combo-removable-btn close'
9386 cls : 'has-feedback',
9395 if(this.removable && !this.editable && !this.tickable){
9397 cls : 'roo-removable',
9403 cls : 'roo-combo-removable-btn close'
9410 if (this.before || this.after) {
9413 cls : 'input-group',
9417 inputblock.cn.push({
9419 cls : 'input-group-addon',
9424 inputblock.cn.push(input);
9426 if(this.hasFeedback && !this.allowBlank){
9427 inputblock.cls += ' has-feedback';
9428 inputblock.cn.push(feedback);
9432 inputblock.cn.push({
9434 cls : 'input-group-addon',
9447 cls: 'form-hidden-field'
9461 cls: 'form-hidden-field'
9465 cls: 'roo-select2-choices',
9469 cls: 'roo-select2-search-field',
9482 cls: 'roo-select2-container input-group',
9487 // cls: 'typeahead typeahead-long dropdown-menu',
9488 // style: 'display:none'
9493 if(!this.multiple && this.showToggleBtn){
9499 if (this.caret != false) {
9502 cls: 'fa fa-' + this.caret
9509 cls : 'input-group-addon btn dropdown-toggle',
9514 cls: 'combobox-clear',
9528 combobox.cls += ' roo-select2-container-multi';
9531 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9533 // Roo.log("left and has label");
9537 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9538 tooltip : 'This field is required'
9543 cls : 'control-label col-sm-' + this.labelWidth,
9544 html : this.fieldLabel
9548 cls : "col-sm-" + (12 - this.labelWidth),
9556 if(this.indicatorpos == 'right'){
9561 cls : 'control-label col-sm-' + this.labelWidth,
9562 html : this.fieldLabel
9567 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9568 tooltip : 'This field is required'
9571 cls : "col-sm-" + (12 - this.labelWidth),
9580 } else if ( this.fieldLabel.length) {
9581 // Roo.log(" label");
9585 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9586 tooltip : 'This field is required'
9590 //cls : 'input-group-addon',
9591 html : this.fieldLabel
9599 if(this.indicatorpos == 'right'){
9604 //cls : 'input-group-addon',
9605 html : this.fieldLabel
9610 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9611 tooltip : 'This field is required'
9622 // Roo.log(" no label && no align");
9629 ['xs','sm','md','lg'].map(function(size){
9630 if (settings[size]) {
9631 cfg.cls += ' col-' + size + '-' + settings[size];
9642 onResize : function(w, h){
9643 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9644 // if(typeof w == 'number'){
9645 // var x = w - this.trigger.getWidth();
9646 // this.inputEl().setWidth(this.adjustWidth('input', x));
9647 // this.trigger.setStyle('left', x+'px');
9652 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9655 getResizeEl : function(){
9656 return this.inputEl();
9660 getPositionEl : function(){
9661 return this.inputEl();
9665 alignErrorIcon : function(){
9666 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9670 initEvents : function(){
9674 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9675 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9676 if(!this.multiple && this.showToggleBtn){
9677 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9678 if(this.hideTrigger){
9679 this.trigger.setDisplayed(false);
9681 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9685 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9688 if(this.removable && !this.editable && !this.tickable){
9689 var close = this.closeTriggerEl();
9692 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9693 close.on('click', this.removeBtnClick, this, close);
9697 //this.trigger.addClassOnOver('x-form-trigger-over');
9698 //this.trigger.addClassOnClick('x-form-trigger-click');
9701 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9705 closeTriggerEl : function()
9707 var close = this.el.select('.roo-combo-removable-btn', true).first();
9708 return close ? close : false;
9711 removeBtnClick : function(e, h, el)
9715 if(this.fireEvent("remove", this) !== false){
9717 this.fireEvent("afterremove", this)
9721 createList : function()
9723 this.list = Roo.get(document.body).createChild({
9725 cls: 'typeahead typeahead-long dropdown-menu',
9726 style: 'display:none'
9729 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9734 initTrigger : function(){
9739 onDestroy : function(){
9741 this.trigger.removeAllListeners();
9742 // this.trigger.remove();
9745 // this.wrap.remove();
9747 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9751 onFocus : function(){
9752 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9755 this.wrap.addClass('x-trigger-wrap-focus');
9756 this.mimicing = true;
9757 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9758 if(this.monitorTab){
9759 this.el.on("keydown", this.checkTab, this);
9766 checkTab : function(e){
9767 if(e.getKey() == e.TAB){
9773 onBlur : function(){
9778 mimicBlur : function(e, t){
9780 if(!this.wrap.contains(t) && this.validateBlur()){
9787 triggerBlur : function(){
9788 this.mimicing = false;
9789 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9790 if(this.monitorTab){
9791 this.el.un("keydown", this.checkTab, this);
9793 //this.wrap.removeClass('x-trigger-wrap-focus');
9794 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9798 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9799 validateBlur : function(e, t){
9804 onDisable : function(){
9805 this.inputEl().dom.disabled = true;
9806 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9808 // this.wrap.addClass('x-item-disabled');
9813 onEnable : function(){
9814 this.inputEl().dom.disabled = false;
9815 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9817 // this.el.removeClass('x-item-disabled');
9822 onShow : function(){
9823 var ae = this.getActionEl();
9826 ae.dom.style.display = '';
9827 ae.dom.style.visibility = 'visible';
9833 onHide : function(){
9834 var ae = this.getActionEl();
9835 ae.dom.style.display = 'none';
9839 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9840 * by an implementing function.
9842 * @param {EventObject} e
9844 onTriggerClick : Roo.emptyFn
9848 * Ext JS Library 1.1.1
9849 * Copyright(c) 2006-2007, Ext JS, LLC.
9851 * Originally Released Under LGPL - original licence link has changed is not relivant.
9854 * <script type="text/javascript">
9859 * @class Roo.data.SortTypes
9861 * Defines the default sorting (casting?) comparison functions used when sorting data.
9863 Roo.data.SortTypes = {
9865 * Default sort that does nothing
9866 * @param {Mixed} s The value being converted
9867 * @return {Mixed} The comparison value
9874 * The regular expression used to strip tags
9878 stripTagsRE : /<\/?[^>]+>/gi,
9881 * Strips all HTML tags to sort on text only
9882 * @param {Mixed} s The value being converted
9883 * @return {String} The comparison value
9885 asText : function(s){
9886 return String(s).replace(this.stripTagsRE, "");
9890 * Strips all HTML tags to sort on text only - Case insensitive
9891 * @param {Mixed} s The value being converted
9892 * @return {String} The comparison value
9894 asUCText : function(s){
9895 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9899 * Case insensitive string
9900 * @param {Mixed} s The value being converted
9901 * @return {String} The comparison value
9903 asUCString : function(s) {
9904 return String(s).toUpperCase();
9909 * @param {Mixed} s The value being converted
9910 * @return {Number} The comparison value
9912 asDate : function(s) {
9916 if(s instanceof Date){
9919 return Date.parse(String(s));
9924 * @param {Mixed} s The value being converted
9925 * @return {Float} The comparison value
9927 asFloat : function(s) {
9928 var val = parseFloat(String(s).replace(/,/g, ""));
9937 * @param {Mixed} s The value being converted
9938 * @return {Number} The comparison value
9940 asInt : function(s) {
9941 var val = parseInt(String(s).replace(/,/g, ""));
9949 * Ext JS Library 1.1.1
9950 * Copyright(c) 2006-2007, Ext JS, LLC.
9952 * Originally Released Under LGPL - original licence link has changed is not relivant.
9955 * <script type="text/javascript">
9959 * @class Roo.data.Record
9960 * Instances of this class encapsulate both record <em>definition</em> information, and record
9961 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9962 * to access Records cached in an {@link Roo.data.Store} object.<br>
9964 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9965 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9968 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9970 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9971 * {@link #create}. The parameters are the same.
9972 * @param {Array} data An associative Array of data values keyed by the field name.
9973 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9974 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9975 * not specified an integer id is generated.
9977 Roo.data.Record = function(data, id){
9978 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9983 * Generate a constructor for a specific record layout.
9984 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9985 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9986 * Each field definition object may contain the following properties: <ul>
9987 * <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,
9988 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9989 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9990 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9991 * is being used, then this is a string containing the javascript expression to reference the data relative to
9992 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9993 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9994 * this may be omitted.</p></li>
9995 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9996 * <ul><li>auto (Default, implies no conversion)</li>
10001 * <li>date</li></ul></p></li>
10002 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10003 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10004 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10005 * by the Reader into an object that will be stored in the Record. It is passed the
10006 * following parameters:<ul>
10007 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10009 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10011 * <br>usage:<br><pre><code>
10012 var TopicRecord = Roo.data.Record.create(
10013 {name: 'title', mapping: 'topic_title'},
10014 {name: 'author', mapping: 'username'},
10015 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10016 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10017 {name: 'lastPoster', mapping: 'user2'},
10018 {name: 'excerpt', mapping: 'post_text'}
10021 var myNewRecord = new TopicRecord({
10022 title: 'Do my job please',
10025 lastPost: new Date(),
10026 lastPoster: 'Animal',
10027 excerpt: 'No way dude!'
10029 myStore.add(myNewRecord);
10034 Roo.data.Record.create = function(o){
10035 var f = function(){
10036 f.superclass.constructor.apply(this, arguments);
10038 Roo.extend(f, Roo.data.Record);
10039 var p = f.prototype;
10040 p.fields = new Roo.util.MixedCollection(false, function(field){
10043 for(var i = 0, len = o.length; i < len; i++){
10044 p.fields.add(new Roo.data.Field(o[i]));
10046 f.getField = function(name){
10047 return p.fields.get(name);
10052 Roo.data.Record.AUTO_ID = 1000;
10053 Roo.data.Record.EDIT = 'edit';
10054 Roo.data.Record.REJECT = 'reject';
10055 Roo.data.Record.COMMIT = 'commit';
10057 Roo.data.Record.prototype = {
10059 * Readonly flag - true if this record has been modified.
10068 join : function(store){
10069 this.store = store;
10073 * Set the named field to the specified value.
10074 * @param {String} name The name of the field to set.
10075 * @param {Object} value The value to set the field to.
10077 set : function(name, value){
10078 if(this.data[name] == value){
10082 if(!this.modified){
10083 this.modified = {};
10085 if(typeof this.modified[name] == 'undefined'){
10086 this.modified[name] = this.data[name];
10088 this.data[name] = value;
10089 if(!this.editing && this.store){
10090 this.store.afterEdit(this);
10095 * Get the value of the named field.
10096 * @param {String} name The name of the field to get the value of.
10097 * @return {Object} The value of the field.
10099 get : function(name){
10100 return this.data[name];
10104 beginEdit : function(){
10105 this.editing = true;
10106 this.modified = {};
10110 cancelEdit : function(){
10111 this.editing = false;
10112 delete this.modified;
10116 endEdit : function(){
10117 this.editing = false;
10118 if(this.dirty && this.store){
10119 this.store.afterEdit(this);
10124 * Usually called by the {@link Roo.data.Store} which owns the Record.
10125 * Rejects all changes made to the Record since either creation, or the last commit operation.
10126 * Modified fields are reverted to their original values.
10128 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10129 * of reject operations.
10131 reject : function(){
10132 var m = this.modified;
10134 if(typeof m[n] != "function"){
10135 this.data[n] = m[n];
10138 this.dirty = false;
10139 delete this.modified;
10140 this.editing = false;
10142 this.store.afterReject(this);
10147 * Usually called by the {@link Roo.data.Store} which owns the Record.
10148 * Commits all changes made to the Record since either creation, or the last commit operation.
10150 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10151 * of commit operations.
10153 commit : function(){
10154 this.dirty = false;
10155 delete this.modified;
10156 this.editing = false;
10158 this.store.afterCommit(this);
10163 hasError : function(){
10164 return this.error != null;
10168 clearError : function(){
10173 * Creates a copy of this record.
10174 * @param {String} id (optional) A new record id if you don't want to use this record's id
10177 copy : function(newId) {
10178 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10182 * Ext JS Library 1.1.1
10183 * Copyright(c) 2006-2007, Ext JS, LLC.
10185 * Originally Released Under LGPL - original licence link has changed is not relivant.
10188 * <script type="text/javascript">
10194 * @class Roo.data.Store
10195 * @extends Roo.util.Observable
10196 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10197 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10199 * 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
10200 * has no knowledge of the format of the data returned by the Proxy.<br>
10202 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10203 * instances from the data object. These records are cached and made available through accessor functions.
10205 * Creates a new Store.
10206 * @param {Object} config A config object containing the objects needed for the Store to access data,
10207 * and read the data into Records.
10209 Roo.data.Store = function(config){
10210 this.data = new Roo.util.MixedCollection(false);
10211 this.data.getKey = function(o){
10214 this.baseParams = {};
10216 this.paramNames = {
10221 "multisort" : "_multisort"
10224 if(config && config.data){
10225 this.inlineData = config.data;
10226 delete config.data;
10229 Roo.apply(this, config);
10231 if(this.reader){ // reader passed
10232 this.reader = Roo.factory(this.reader, Roo.data);
10233 this.reader.xmodule = this.xmodule || false;
10234 if(!this.recordType){
10235 this.recordType = this.reader.recordType;
10237 if(this.reader.onMetaChange){
10238 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10242 if(this.recordType){
10243 this.fields = this.recordType.prototype.fields;
10245 this.modified = [];
10249 * @event datachanged
10250 * Fires when the data cache has changed, and a widget which is using this Store
10251 * as a Record cache should refresh its view.
10252 * @param {Store} this
10254 datachanged : true,
10256 * @event metachange
10257 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10258 * @param {Store} this
10259 * @param {Object} meta The JSON metadata
10264 * Fires when Records have been added to the Store
10265 * @param {Store} this
10266 * @param {Roo.data.Record[]} records The array of Records added
10267 * @param {Number} index The index at which the record(s) were added
10272 * Fires when a Record has been removed from the Store
10273 * @param {Store} this
10274 * @param {Roo.data.Record} record The Record that was removed
10275 * @param {Number} index The index at which the record was removed
10280 * Fires when a Record has been updated
10281 * @param {Store} this
10282 * @param {Roo.data.Record} record The Record that was updated
10283 * @param {String} operation The update operation being performed. Value may be one of:
10285 Roo.data.Record.EDIT
10286 Roo.data.Record.REJECT
10287 Roo.data.Record.COMMIT
10293 * Fires when the data cache has been cleared.
10294 * @param {Store} this
10298 * @event beforeload
10299 * Fires before a request is made for a new data object. If the beforeload handler returns false
10300 * the load action will be canceled.
10301 * @param {Store} this
10302 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10306 * @event beforeloadadd
10307 * Fires after a new set of Records has been loaded.
10308 * @param {Store} this
10309 * @param {Roo.data.Record[]} records The Records that were loaded
10310 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10312 beforeloadadd : true,
10315 * Fires after a new set of Records has been loaded, before they are added to the store.
10316 * @param {Store} this
10317 * @param {Roo.data.Record[]} records The Records that were loaded
10318 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10319 * @params {Object} return from reader
10323 * @event loadexception
10324 * Fires if an exception occurs in the Proxy during loading.
10325 * Called with the signature of the Proxy's "loadexception" event.
10326 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10329 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10330 * @param {Object} load options
10331 * @param {Object} jsonData from your request (normally this contains the Exception)
10333 loadexception : true
10337 this.proxy = Roo.factory(this.proxy, Roo.data);
10338 this.proxy.xmodule = this.xmodule || false;
10339 this.relayEvents(this.proxy, ["loadexception"]);
10341 this.sortToggle = {};
10342 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10344 Roo.data.Store.superclass.constructor.call(this);
10346 if(this.inlineData){
10347 this.loadData(this.inlineData);
10348 delete this.inlineData;
10352 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10354 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10355 * without a remote query - used by combo/forms at present.
10359 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10362 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10365 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10366 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10369 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10370 * on any HTTP request
10373 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10376 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10380 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10381 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10383 remoteSort : false,
10386 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10387 * loaded or when a record is removed. (defaults to false).
10389 pruneModifiedRecords : false,
10392 lastOptions : null,
10395 * Add Records to the Store and fires the add event.
10396 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10398 add : function(records){
10399 records = [].concat(records);
10400 for(var i = 0, len = records.length; i < len; i++){
10401 records[i].join(this);
10403 var index = this.data.length;
10404 this.data.addAll(records);
10405 this.fireEvent("add", this, records, index);
10409 * Remove a Record from the Store and fires the remove event.
10410 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10412 remove : function(record){
10413 var index = this.data.indexOf(record);
10414 this.data.removeAt(index);
10415 if(this.pruneModifiedRecords){
10416 this.modified.remove(record);
10418 this.fireEvent("remove", this, record, index);
10422 * Remove all Records from the Store and fires the clear event.
10424 removeAll : function(){
10426 if(this.pruneModifiedRecords){
10427 this.modified = [];
10429 this.fireEvent("clear", this);
10433 * Inserts Records to the Store at the given index and fires the add event.
10434 * @param {Number} index The start index at which to insert the passed Records.
10435 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10437 insert : function(index, records){
10438 records = [].concat(records);
10439 for(var i = 0, len = records.length; i < len; i++){
10440 this.data.insert(index, records[i]);
10441 records[i].join(this);
10443 this.fireEvent("add", this, records, index);
10447 * Get the index within the cache of the passed Record.
10448 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10449 * @return {Number} The index of the passed Record. Returns -1 if not found.
10451 indexOf : function(record){
10452 return this.data.indexOf(record);
10456 * Get the index within the cache of the Record with the passed id.
10457 * @param {String} id The id of the Record to find.
10458 * @return {Number} The index of the Record. Returns -1 if not found.
10460 indexOfId : function(id){
10461 return this.data.indexOfKey(id);
10465 * Get the Record with the specified id.
10466 * @param {String} id The id of the Record to find.
10467 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10469 getById : function(id){
10470 return this.data.key(id);
10474 * Get the Record at the specified index.
10475 * @param {Number} index The index of the Record to find.
10476 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10478 getAt : function(index){
10479 return this.data.itemAt(index);
10483 * Returns a range of Records between specified indices.
10484 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10485 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10486 * @return {Roo.data.Record[]} An array of Records
10488 getRange : function(start, end){
10489 return this.data.getRange(start, end);
10493 storeOptions : function(o){
10494 o = Roo.apply({}, o);
10497 this.lastOptions = o;
10501 * Loads the Record cache from the configured Proxy using the configured Reader.
10503 * If using remote paging, then the first load call must specify the <em>start</em>
10504 * and <em>limit</em> properties in the options.params property to establish the initial
10505 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10507 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10508 * and this call will return before the new data has been loaded. Perform any post-processing
10509 * in a callback function, or in a "load" event handler.</strong>
10511 * @param {Object} options An object containing properties which control loading options:<ul>
10512 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10513 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10514 * passed the following arguments:<ul>
10515 * <li>r : Roo.data.Record[]</li>
10516 * <li>options: Options object from the load call</li>
10517 * <li>success: Boolean success indicator</li></ul></li>
10518 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10519 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10522 load : function(options){
10523 options = options || {};
10524 if(this.fireEvent("beforeload", this, options) !== false){
10525 this.storeOptions(options);
10526 var p = Roo.apply(options.params || {}, this.baseParams);
10527 // if meta was not loaded from remote source.. try requesting it.
10528 if (!this.reader.metaFromRemote) {
10529 p._requestMeta = 1;
10531 if(this.sortInfo && this.remoteSort){
10532 var pn = this.paramNames;
10533 p[pn["sort"]] = this.sortInfo.field;
10534 p[pn["dir"]] = this.sortInfo.direction;
10536 if (this.multiSort) {
10537 var pn = this.paramNames;
10538 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10541 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10546 * Reloads the Record cache from the configured Proxy using the configured Reader and
10547 * the options from the last load operation performed.
10548 * @param {Object} options (optional) An object containing properties which may override the options
10549 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10550 * the most recently used options are reused).
10552 reload : function(options){
10553 this.load(Roo.applyIf(options||{}, this.lastOptions));
10557 // Called as a callback by the Reader during a load operation.
10558 loadRecords : function(o, options, success){
10559 if(!o || success === false){
10560 if(success !== false){
10561 this.fireEvent("load", this, [], options, o);
10563 if(options.callback){
10564 options.callback.call(options.scope || this, [], options, false);
10568 // if data returned failure - throw an exception.
10569 if (o.success === false) {
10570 // show a message if no listener is registered.
10571 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10572 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10574 // loadmask wil be hooked into this..
10575 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10578 var r = o.records, t = o.totalRecords || r.length;
10580 this.fireEvent("beforeloadadd", this, r, options, o);
10582 if(!options || options.add !== true){
10583 if(this.pruneModifiedRecords){
10584 this.modified = [];
10586 for(var i = 0, len = r.length; i < len; i++){
10590 this.data = this.snapshot;
10591 delete this.snapshot;
10594 this.data.addAll(r);
10595 this.totalLength = t;
10597 this.fireEvent("datachanged", this);
10599 this.totalLength = Math.max(t, this.data.length+r.length);
10602 this.fireEvent("load", this, r, options, o);
10603 if(options.callback){
10604 options.callback.call(options.scope || this, r, options, true);
10610 * Loads data from a passed data block. A Reader which understands the format of the data
10611 * must have been configured in the constructor.
10612 * @param {Object} data The data block from which to read the Records. The format of the data expected
10613 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10614 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10616 loadData : function(o, append){
10617 var r = this.reader.readRecords(o);
10618 this.loadRecords(r, {add: append}, true);
10622 * Gets the number of cached records.
10624 * <em>If using paging, this may not be the total size of the dataset. If the data object
10625 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10626 * the data set size</em>
10628 getCount : function(){
10629 return this.data.length || 0;
10633 * Gets the total number of records in the dataset as returned by the server.
10635 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10636 * the dataset size</em>
10638 getTotalCount : function(){
10639 return this.totalLength || 0;
10643 * Returns the sort state of the Store as an object with two properties:
10645 field {String} The name of the field by which the Records are sorted
10646 direction {String} The sort order, "ASC" or "DESC"
10649 getSortState : function(){
10650 return this.sortInfo;
10654 applySort : function(){
10655 if(this.sortInfo && !this.remoteSort){
10656 var s = this.sortInfo, f = s.field;
10657 var st = this.fields.get(f).sortType;
10658 var fn = function(r1, r2){
10659 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10660 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10662 this.data.sort(s.direction, fn);
10663 if(this.snapshot && this.snapshot != this.data){
10664 this.snapshot.sort(s.direction, fn);
10670 * Sets the default sort column and order to be used by the next load operation.
10671 * @param {String} fieldName The name of the field to sort by.
10672 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10674 setDefaultSort : function(field, dir){
10675 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10679 * Sort the Records.
10680 * If remote sorting is used, the sort is performed on the server, and the cache is
10681 * reloaded. If local sorting is used, the cache is sorted internally.
10682 * @param {String} fieldName The name of the field to sort by.
10683 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10685 sort : function(fieldName, dir){
10686 var f = this.fields.get(fieldName);
10688 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10690 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10691 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10696 this.sortToggle[f.name] = dir;
10697 this.sortInfo = {field: f.name, direction: dir};
10698 if(!this.remoteSort){
10700 this.fireEvent("datachanged", this);
10702 this.load(this.lastOptions);
10707 * Calls the specified function for each of the Records in the cache.
10708 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10709 * Returning <em>false</em> aborts and exits the iteration.
10710 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10712 each : function(fn, scope){
10713 this.data.each(fn, scope);
10717 * Gets all records modified since the last commit. Modified records are persisted across load operations
10718 * (e.g., during paging).
10719 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10721 getModifiedRecords : function(){
10722 return this.modified;
10726 createFilterFn : function(property, value, anyMatch){
10727 if(!value.exec){ // not a regex
10728 value = String(value);
10729 if(value.length == 0){
10732 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10734 return function(r){
10735 return value.test(r.data[property]);
10740 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10741 * @param {String} property A field on your records
10742 * @param {Number} start The record index to start at (defaults to 0)
10743 * @param {Number} end The last record index to include (defaults to length - 1)
10744 * @return {Number} The sum
10746 sum : function(property, start, end){
10747 var rs = this.data.items, v = 0;
10748 start = start || 0;
10749 end = (end || end === 0) ? end : rs.length-1;
10751 for(var i = start; i <= end; i++){
10752 v += (rs[i].data[property] || 0);
10758 * Filter the records by a specified property.
10759 * @param {String} field A field on your records
10760 * @param {String/RegExp} value Either a string that the field
10761 * should start with or a RegExp to test against the field
10762 * @param {Boolean} anyMatch True to match any part not just the beginning
10764 filter : function(property, value, anyMatch){
10765 var fn = this.createFilterFn(property, value, anyMatch);
10766 return fn ? this.filterBy(fn) : this.clearFilter();
10770 * Filter by a function. The specified function will be called with each
10771 * record in this data source. If the function returns true the record is included,
10772 * otherwise it is filtered.
10773 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10774 * @param {Object} scope (optional) The scope of the function (defaults to this)
10776 filterBy : function(fn, scope){
10777 this.snapshot = this.snapshot || this.data;
10778 this.data = this.queryBy(fn, scope||this);
10779 this.fireEvent("datachanged", this);
10783 * Query the records by a specified property.
10784 * @param {String} field A field on your records
10785 * @param {String/RegExp} value Either a string that the field
10786 * should start with or a RegExp to test against the field
10787 * @param {Boolean} anyMatch True to match any part not just the beginning
10788 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10790 query : function(property, value, anyMatch){
10791 var fn = this.createFilterFn(property, value, anyMatch);
10792 return fn ? this.queryBy(fn) : this.data.clone();
10796 * Query by a function. The specified function will be called with each
10797 * record in this data source. If the function returns true the record is included
10799 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10800 * @param {Object} scope (optional) The scope of the function (defaults to this)
10801 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10803 queryBy : function(fn, scope){
10804 var data = this.snapshot || this.data;
10805 return data.filterBy(fn, scope||this);
10809 * Collects unique values for a particular dataIndex from this store.
10810 * @param {String} dataIndex The property to collect
10811 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10812 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10813 * @return {Array} An array of the unique values
10815 collect : function(dataIndex, allowNull, bypassFilter){
10816 var d = (bypassFilter === true && this.snapshot) ?
10817 this.snapshot.items : this.data.items;
10818 var v, sv, r = [], l = {};
10819 for(var i = 0, len = d.length; i < len; i++){
10820 v = d[i].data[dataIndex];
10822 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10831 * Revert to a view of the Record cache with no filtering applied.
10832 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10834 clearFilter : function(suppressEvent){
10835 if(this.snapshot && this.snapshot != this.data){
10836 this.data = this.snapshot;
10837 delete this.snapshot;
10838 if(suppressEvent !== true){
10839 this.fireEvent("datachanged", this);
10845 afterEdit : function(record){
10846 if(this.modified.indexOf(record) == -1){
10847 this.modified.push(record);
10849 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10853 afterReject : function(record){
10854 this.modified.remove(record);
10855 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10859 afterCommit : function(record){
10860 this.modified.remove(record);
10861 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10865 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10866 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10868 commitChanges : function(){
10869 var m = this.modified.slice(0);
10870 this.modified = [];
10871 for(var i = 0, len = m.length; i < len; i++){
10877 * Cancel outstanding changes on all changed records.
10879 rejectChanges : function(){
10880 var m = this.modified.slice(0);
10881 this.modified = [];
10882 for(var i = 0, len = m.length; i < len; i++){
10887 onMetaChange : function(meta, rtype, o){
10888 this.recordType = rtype;
10889 this.fields = rtype.prototype.fields;
10890 delete this.snapshot;
10891 this.sortInfo = meta.sortInfo || this.sortInfo;
10892 this.modified = [];
10893 this.fireEvent('metachange', this, this.reader.meta);
10896 moveIndex : function(data, type)
10898 var index = this.indexOf(data);
10900 var newIndex = index + type;
10904 this.insert(newIndex, data);
10909 * Ext JS Library 1.1.1
10910 * Copyright(c) 2006-2007, Ext JS, LLC.
10912 * Originally Released Under LGPL - original licence link has changed is not relivant.
10915 * <script type="text/javascript">
10919 * @class Roo.data.SimpleStore
10920 * @extends Roo.data.Store
10921 * Small helper class to make creating Stores from Array data easier.
10922 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10923 * @cfg {Array} fields An array of field definition objects, or field name strings.
10924 * @cfg {Array} data The multi-dimensional array of data
10926 * @param {Object} config
10928 Roo.data.SimpleStore = function(config){
10929 Roo.data.SimpleStore.superclass.constructor.call(this, {
10931 reader: new Roo.data.ArrayReader({
10934 Roo.data.Record.create(config.fields)
10936 proxy : new Roo.data.MemoryProxy(config.data)
10940 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10942 * Ext JS Library 1.1.1
10943 * Copyright(c) 2006-2007, Ext JS, LLC.
10945 * Originally Released Under LGPL - original licence link has changed is not relivant.
10948 * <script type="text/javascript">
10953 * @extends Roo.data.Store
10954 * @class Roo.data.JsonStore
10955 * Small helper class to make creating Stores for JSON data easier. <br/>
10957 var store = new Roo.data.JsonStore({
10958 url: 'get-images.php',
10960 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10963 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10964 * JsonReader and HttpProxy (unless inline data is provided).</b>
10965 * @cfg {Array} fields An array of field definition objects, or field name strings.
10967 * @param {Object} config
10969 Roo.data.JsonStore = function(c){
10970 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10971 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10972 reader: new Roo.data.JsonReader(c, c.fields)
10975 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10977 * Ext JS Library 1.1.1
10978 * Copyright(c) 2006-2007, Ext JS, LLC.
10980 * Originally Released Under LGPL - original licence link has changed is not relivant.
10983 * <script type="text/javascript">
10987 Roo.data.Field = function(config){
10988 if(typeof config == "string"){
10989 config = {name: config};
10991 Roo.apply(this, config);
10994 this.type = "auto";
10997 var st = Roo.data.SortTypes;
10998 // named sortTypes are supported, here we look them up
10999 if(typeof this.sortType == "string"){
11000 this.sortType = st[this.sortType];
11003 // set default sortType for strings and dates
11004 if(!this.sortType){
11007 this.sortType = st.asUCString;
11010 this.sortType = st.asDate;
11013 this.sortType = st.none;
11018 var stripRe = /[\$,%]/g;
11020 // prebuilt conversion function for this field, instead of
11021 // switching every time we're reading a value
11023 var cv, dateFormat = this.dateFormat;
11028 cv = function(v){ return v; };
11031 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11035 return v !== undefined && v !== null && v !== '' ?
11036 parseInt(String(v).replace(stripRe, ""), 10) : '';
11041 return v !== undefined && v !== null && v !== '' ?
11042 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11047 cv = function(v){ return v === true || v === "true" || v == 1; };
11054 if(v instanceof Date){
11058 if(dateFormat == "timestamp"){
11059 return new Date(v*1000);
11061 return Date.parseDate(v, dateFormat);
11063 var parsed = Date.parse(v);
11064 return parsed ? new Date(parsed) : null;
11073 Roo.data.Field.prototype = {
11081 * Ext JS Library 1.1.1
11082 * Copyright(c) 2006-2007, Ext JS, LLC.
11084 * Originally Released Under LGPL - original licence link has changed is not relivant.
11087 * <script type="text/javascript">
11090 // Base class for reading structured data from a data source. This class is intended to be
11091 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11094 * @class Roo.data.DataReader
11095 * Base class for reading structured data from a data source. This class is intended to be
11096 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11099 Roo.data.DataReader = function(meta, recordType){
11103 this.recordType = recordType instanceof Array ?
11104 Roo.data.Record.create(recordType) : recordType;
11107 Roo.data.DataReader.prototype = {
11109 * Create an empty record
11110 * @param {Object} data (optional) - overlay some values
11111 * @return {Roo.data.Record} record created.
11113 newRow : function(d) {
11115 this.recordType.prototype.fields.each(function(c) {
11117 case 'int' : da[c.name] = 0; break;
11118 case 'date' : da[c.name] = new Date(); break;
11119 case 'float' : da[c.name] = 0.0; break;
11120 case 'boolean' : da[c.name] = false; break;
11121 default : da[c.name] = ""; break;
11125 return new this.recordType(Roo.apply(da, d));
11130 * Ext JS Library 1.1.1
11131 * Copyright(c) 2006-2007, Ext JS, LLC.
11133 * Originally Released Under LGPL - original licence link has changed is not relivant.
11136 * <script type="text/javascript">
11140 * @class Roo.data.DataProxy
11141 * @extends Roo.data.Observable
11142 * This class is an abstract base class for implementations which provide retrieval of
11143 * unformatted data objects.<br>
11145 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11146 * (of the appropriate type which knows how to parse the data object) to provide a block of
11147 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11149 * Custom implementations must implement the load method as described in
11150 * {@link Roo.data.HttpProxy#load}.
11152 Roo.data.DataProxy = function(){
11155 * @event beforeload
11156 * Fires before a network request is made to retrieve a data object.
11157 * @param {Object} This DataProxy object.
11158 * @param {Object} params The params parameter to the load function.
11163 * Fires before the load method's callback is called.
11164 * @param {Object} This DataProxy object.
11165 * @param {Object} o The data object.
11166 * @param {Object} arg The callback argument object passed to the load function.
11170 * @event loadexception
11171 * Fires if an Exception occurs during data retrieval.
11172 * @param {Object} This DataProxy object.
11173 * @param {Object} o The data object.
11174 * @param {Object} arg The callback argument object passed to the load function.
11175 * @param {Object} e The Exception.
11177 loadexception : true
11179 Roo.data.DataProxy.superclass.constructor.call(this);
11182 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11185 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11189 * Ext JS Library 1.1.1
11190 * Copyright(c) 2006-2007, Ext JS, LLC.
11192 * Originally Released Under LGPL - original licence link has changed is not relivant.
11195 * <script type="text/javascript">
11198 * @class Roo.data.MemoryProxy
11199 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11200 * to the Reader when its load method is called.
11202 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11204 Roo.data.MemoryProxy = function(data){
11208 Roo.data.MemoryProxy.superclass.constructor.call(this);
11212 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11215 * Load data from the requested source (in this case an in-memory
11216 * data object passed to the constructor), read the data object into
11217 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11218 * process that block using the passed callback.
11219 * @param {Object} params This parameter is not used by the MemoryProxy class.
11220 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11221 * object into a block of Roo.data.Records.
11222 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11223 * The function must be passed <ul>
11224 * <li>The Record block object</li>
11225 * <li>The "arg" argument from the load function</li>
11226 * <li>A boolean success indicator</li>
11228 * @param {Object} scope The scope in which to call the callback
11229 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11231 load : function(params, reader, callback, scope, arg){
11232 params = params || {};
11235 result = reader.readRecords(this.data);
11237 this.fireEvent("loadexception", this, arg, null, e);
11238 callback.call(scope, null, arg, false);
11241 callback.call(scope, result, arg, true);
11245 update : function(params, records){
11250 * Ext JS Library 1.1.1
11251 * Copyright(c) 2006-2007, Ext JS, LLC.
11253 * Originally Released Under LGPL - original licence link has changed is not relivant.
11256 * <script type="text/javascript">
11259 * @class Roo.data.HttpProxy
11260 * @extends Roo.data.DataProxy
11261 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11262 * configured to reference a certain URL.<br><br>
11264 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11265 * from which the running page was served.<br><br>
11267 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11269 * Be aware that to enable the browser to parse an XML document, the server must set
11270 * the Content-Type header in the HTTP response to "text/xml".
11272 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11273 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11274 * will be used to make the request.
11276 Roo.data.HttpProxy = function(conn){
11277 Roo.data.HttpProxy.superclass.constructor.call(this);
11278 // is conn a conn config or a real conn?
11280 this.useAjax = !conn || !conn.events;
11284 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11285 // thse are take from connection...
11288 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11291 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11292 * extra parameters to each request made by this object. (defaults to undefined)
11295 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11296 * to each request made by this object. (defaults to undefined)
11299 * @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)
11302 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11305 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11311 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11315 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11316 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11317 * a finer-grained basis than the DataProxy events.
11319 getConnection : function(){
11320 return this.useAjax ? Roo.Ajax : this.conn;
11324 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11325 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11326 * process that block using the passed callback.
11327 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11328 * for the request to the remote server.
11329 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11330 * object into a block of Roo.data.Records.
11331 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11332 * The function must be passed <ul>
11333 * <li>The Record block object</li>
11334 * <li>The "arg" argument from the load function</li>
11335 * <li>A boolean success indicator</li>
11337 * @param {Object} scope The scope in which to call the callback
11338 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11340 load : function(params, reader, callback, scope, arg){
11341 if(this.fireEvent("beforeload", this, params) !== false){
11343 params : params || {},
11345 callback : callback,
11350 callback : this.loadResponse,
11354 Roo.applyIf(o, this.conn);
11355 if(this.activeRequest){
11356 Roo.Ajax.abort(this.activeRequest);
11358 this.activeRequest = Roo.Ajax.request(o);
11360 this.conn.request(o);
11363 callback.call(scope||this, null, arg, false);
11368 loadResponse : function(o, success, response){
11369 delete this.activeRequest;
11371 this.fireEvent("loadexception", this, o, response);
11372 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11377 result = o.reader.read(response);
11379 this.fireEvent("loadexception", this, o, response, e);
11380 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11384 this.fireEvent("load", this, o, o.request.arg);
11385 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11389 update : function(dataSet){
11394 updateResponse : function(dataSet){
11399 * Ext JS Library 1.1.1
11400 * Copyright(c) 2006-2007, Ext JS, LLC.
11402 * Originally Released Under LGPL - original licence link has changed is not relivant.
11405 * <script type="text/javascript">
11409 * @class Roo.data.ScriptTagProxy
11410 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11411 * other than the originating domain of the running page.<br><br>
11413 * <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
11414 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11416 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11417 * source code that is used as the source inside a <script> tag.<br><br>
11419 * In order for the browser to process the returned data, the server must wrap the data object
11420 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11421 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11422 * depending on whether the callback name was passed:
11425 boolean scriptTag = false;
11426 String cb = request.getParameter("callback");
11429 response.setContentType("text/javascript");
11431 response.setContentType("application/x-json");
11433 Writer out = response.getWriter();
11435 out.write(cb + "(");
11437 out.print(dataBlock.toJsonString());
11444 * @param {Object} config A configuration object.
11446 Roo.data.ScriptTagProxy = function(config){
11447 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11448 Roo.apply(this, config);
11449 this.head = document.getElementsByTagName("head")[0];
11452 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11454 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11456 * @cfg {String} url The URL from which to request the data object.
11459 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11463 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11464 * the server the name of the callback function set up by the load call to process the returned data object.
11465 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11466 * javascript output which calls this named function passing the data object as its only parameter.
11468 callbackParam : "callback",
11470 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11471 * name to the request.
11476 * Load data from the configured URL, read the data object into
11477 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11478 * process that block using the passed callback.
11479 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11480 * for the request to the remote server.
11481 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11482 * object into a block of Roo.data.Records.
11483 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11484 * The function must be passed <ul>
11485 * <li>The Record block object</li>
11486 * <li>The "arg" argument from the load function</li>
11487 * <li>A boolean success indicator</li>
11489 * @param {Object} scope The scope in which to call the callback
11490 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11492 load : function(params, reader, callback, scope, arg){
11493 if(this.fireEvent("beforeload", this, params) !== false){
11495 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11497 var url = this.url;
11498 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11500 url += "&_dc=" + (new Date().getTime());
11502 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11505 cb : "stcCallback"+transId,
11506 scriptId : "stcScript"+transId,
11510 callback : callback,
11516 window[trans.cb] = function(o){
11517 conn.handleResponse(o, trans);
11520 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11522 if(this.autoAbort !== false){
11526 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11528 var script = document.createElement("script");
11529 script.setAttribute("src", url);
11530 script.setAttribute("type", "text/javascript");
11531 script.setAttribute("id", trans.scriptId);
11532 this.head.appendChild(script);
11534 this.trans = trans;
11536 callback.call(scope||this, null, arg, false);
11541 isLoading : function(){
11542 return this.trans ? true : false;
11546 * Abort the current server request.
11548 abort : function(){
11549 if(this.isLoading()){
11550 this.destroyTrans(this.trans);
11555 destroyTrans : function(trans, isLoaded){
11556 this.head.removeChild(document.getElementById(trans.scriptId));
11557 clearTimeout(trans.timeoutId);
11559 window[trans.cb] = undefined;
11561 delete window[trans.cb];
11564 // if hasn't been loaded, wait for load to remove it to prevent script error
11565 window[trans.cb] = function(){
11566 window[trans.cb] = undefined;
11568 delete window[trans.cb];
11575 handleResponse : function(o, trans){
11576 this.trans = false;
11577 this.destroyTrans(trans, true);
11580 result = trans.reader.readRecords(o);
11582 this.fireEvent("loadexception", this, o, trans.arg, e);
11583 trans.callback.call(trans.scope||window, null, trans.arg, false);
11586 this.fireEvent("load", this, o, trans.arg);
11587 trans.callback.call(trans.scope||window, result, trans.arg, true);
11591 handleFailure : function(trans){
11592 this.trans = false;
11593 this.destroyTrans(trans, false);
11594 this.fireEvent("loadexception", this, null, trans.arg);
11595 trans.callback.call(trans.scope||window, null, trans.arg, false);
11599 * Ext JS Library 1.1.1
11600 * Copyright(c) 2006-2007, Ext JS, LLC.
11602 * Originally Released Under LGPL - original licence link has changed is not relivant.
11605 * <script type="text/javascript">
11609 * @class Roo.data.JsonReader
11610 * @extends Roo.data.DataReader
11611 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11612 * based on mappings in a provided Roo.data.Record constructor.
11614 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11615 * in the reply previously.
11620 var RecordDef = Roo.data.Record.create([
11621 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11622 {name: 'occupation'} // This field will use "occupation" as the mapping.
11624 var myReader = new Roo.data.JsonReader({
11625 totalProperty: "results", // The property which contains the total dataset size (optional)
11626 root: "rows", // The property which contains an Array of row objects
11627 id: "id" // The property within each row object that provides an ID for the record (optional)
11631 * This would consume a JSON file like this:
11633 { 'results': 2, 'rows': [
11634 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11635 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11638 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11639 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11640 * paged from the remote server.
11641 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11642 * @cfg {String} root name of the property which contains the Array of row objects.
11643 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11644 * @cfg {Array} fields Array of field definition objects
11646 * Create a new JsonReader
11647 * @param {Object} meta Metadata configuration options
11648 * @param {Object} recordType Either an Array of field definition objects,
11649 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11651 Roo.data.JsonReader = function(meta, recordType){
11654 // set some defaults:
11655 Roo.applyIf(meta, {
11656 totalProperty: 'total',
11657 successProperty : 'success',
11662 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11664 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11667 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11668 * Used by Store query builder to append _requestMeta to params.
11671 metaFromRemote : false,
11673 * This method is only used by a DataProxy which has retrieved data from a remote server.
11674 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11675 * @return {Object} data A data block which is used by an Roo.data.Store object as
11676 * a cache of Roo.data.Records.
11678 read : function(response){
11679 var json = response.responseText;
11681 var o = /* eval:var:o */ eval("("+json+")");
11683 throw {message: "JsonReader.read: Json object not found"};
11689 this.metaFromRemote = true;
11690 this.meta = o.metaData;
11691 this.recordType = Roo.data.Record.create(o.metaData.fields);
11692 this.onMetaChange(this.meta, this.recordType, o);
11694 return this.readRecords(o);
11697 // private function a store will implement
11698 onMetaChange : function(meta, recordType, o){
11705 simpleAccess: function(obj, subsc) {
11712 getJsonAccessor: function(){
11714 return function(expr) {
11716 return(re.test(expr))
11717 ? new Function("obj", "return obj." + expr)
11722 return Roo.emptyFn;
11727 * Create a data block containing Roo.data.Records from an XML document.
11728 * @param {Object} o An object which contains an Array of row objects in the property specified
11729 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11730 * which contains the total size of the dataset.
11731 * @return {Object} data A data block which is used by an Roo.data.Store object as
11732 * a cache of Roo.data.Records.
11734 readRecords : function(o){
11736 * After any data loads, the raw JSON data is available for further custom processing.
11740 var s = this.meta, Record = this.recordType,
11741 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11743 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11745 if(s.totalProperty) {
11746 this.getTotal = this.getJsonAccessor(s.totalProperty);
11748 if(s.successProperty) {
11749 this.getSuccess = this.getJsonAccessor(s.successProperty);
11751 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11753 var g = this.getJsonAccessor(s.id);
11754 this.getId = function(rec) {
11756 return (r === undefined || r === "") ? null : r;
11759 this.getId = function(){return null;};
11762 for(var jj = 0; jj < fl; jj++){
11764 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11765 this.ef[jj] = this.getJsonAccessor(map);
11769 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11770 if(s.totalProperty){
11771 var vt = parseInt(this.getTotal(o), 10);
11776 if(s.successProperty){
11777 var vs = this.getSuccess(o);
11778 if(vs === false || vs === 'false'){
11783 for(var i = 0; i < c; i++){
11786 var id = this.getId(n);
11787 for(var j = 0; j < fl; j++){
11789 var v = this.ef[j](n);
11791 Roo.log('missing convert for ' + f.name);
11795 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11797 var record = new Record(values, id);
11799 records[i] = record;
11805 totalRecords : totalRecords
11810 * Ext JS Library 1.1.1
11811 * Copyright(c) 2006-2007, Ext JS, LLC.
11813 * Originally Released Under LGPL - original licence link has changed is not relivant.
11816 * <script type="text/javascript">
11820 * @class Roo.data.ArrayReader
11821 * @extends Roo.data.DataReader
11822 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11823 * Each element of that Array represents a row of data fields. The
11824 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11825 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11829 var RecordDef = Roo.data.Record.create([
11830 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11831 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11833 var myReader = new Roo.data.ArrayReader({
11834 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11838 * This would consume an Array like this:
11840 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11842 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11844 * Create a new JsonReader
11845 * @param {Object} meta Metadata configuration options.
11846 * @param {Object} recordType Either an Array of field definition objects
11847 * as specified to {@link Roo.data.Record#create},
11848 * or an {@link Roo.data.Record} object
11849 * created using {@link Roo.data.Record#create}.
11851 Roo.data.ArrayReader = function(meta, recordType){
11852 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11855 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11857 * Create a data block containing Roo.data.Records from an XML document.
11858 * @param {Object} o An Array of row objects which represents the dataset.
11859 * @return {Object} data A data block which is used by an Roo.data.Store object as
11860 * a cache of Roo.data.Records.
11862 readRecords : function(o){
11863 var sid = this.meta ? this.meta.id : null;
11864 var recordType = this.recordType, fields = recordType.prototype.fields;
11867 for(var i = 0; i < root.length; i++){
11870 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11871 for(var j = 0, jlen = fields.length; j < jlen; j++){
11872 var f = fields.items[j];
11873 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11874 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11876 values[f.name] = v;
11878 var record = new recordType(values, id);
11880 records[records.length] = record;
11884 totalRecords : records.length
11893 * @class Roo.bootstrap.ComboBox
11894 * @extends Roo.bootstrap.TriggerField
11895 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11896 * @cfg {Boolean} append (true|false) default false
11897 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11898 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11899 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11900 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11901 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11902 * @cfg {Boolean} animate default true
11903 * @cfg {Boolean} emptyResultText only for touch device
11904 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11906 * Create a new ComboBox.
11907 * @param {Object} config Configuration options
11909 Roo.bootstrap.ComboBox = function(config){
11910 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11914 * Fires when the dropdown list is expanded
11915 * @param {Roo.bootstrap.ComboBox} combo This combo box
11920 * Fires when the dropdown list is collapsed
11921 * @param {Roo.bootstrap.ComboBox} combo This combo box
11925 * @event beforeselect
11926 * Fires before a list item is selected. Return false to cancel the selection.
11927 * @param {Roo.bootstrap.ComboBox} combo This combo box
11928 * @param {Roo.data.Record} record The data record returned from the underlying store
11929 * @param {Number} index The index of the selected item in the dropdown list
11931 'beforeselect' : true,
11934 * Fires when a list item is selected
11935 * @param {Roo.bootstrap.ComboBox} combo This combo box
11936 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11937 * @param {Number} index The index of the selected item in the dropdown list
11941 * @event beforequery
11942 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11943 * The event object passed has these properties:
11944 * @param {Roo.bootstrap.ComboBox} combo This combo box
11945 * @param {String} query The query
11946 * @param {Boolean} forceAll true to force "all" query
11947 * @param {Boolean} cancel true to cancel the query
11948 * @param {Object} e The query event object
11950 'beforequery': true,
11953 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11954 * @param {Roo.bootstrap.ComboBox} combo This combo box
11959 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11960 * @param {Roo.bootstrap.ComboBox} combo This combo box
11961 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11966 * Fires when the remove value from the combobox array
11967 * @param {Roo.bootstrap.ComboBox} combo This combo box
11971 * @event afterremove
11972 * Fires when the remove value from the combobox array
11973 * @param {Roo.bootstrap.ComboBox} combo This combo box
11975 'afterremove' : true,
11977 * @event specialfilter
11978 * Fires when specialfilter
11979 * @param {Roo.bootstrap.ComboBox} combo This combo box
11981 'specialfilter' : true,
11984 * Fires when tick the element
11985 * @param {Roo.bootstrap.ComboBox} combo This combo box
11989 * @event touchviewdisplay
11990 * Fires when touch view require special display (default is using displayField)
11991 * @param {Roo.bootstrap.ComboBox} combo This combo box
11992 * @param {Object} cfg set html .
11994 'touchviewdisplay' : true
11999 this.tickItems = [];
12001 this.selectedIndex = -1;
12002 if(this.mode == 'local'){
12003 if(config.queryDelay === undefined){
12004 this.queryDelay = 10;
12006 if(config.minChars === undefined){
12012 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12015 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12016 * rendering into an Roo.Editor, defaults to false)
12019 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12020 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12023 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12026 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12027 * the dropdown list (defaults to undefined, with no header element)
12031 * @cfg {String/Roo.Template} tpl The template to use to render the output
12035 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12037 listWidth: undefined,
12039 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12040 * mode = 'remote' or 'text' if mode = 'local')
12042 displayField: undefined,
12045 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12046 * mode = 'remote' or 'value' if mode = 'local').
12047 * Note: use of a valueField requires the user make a selection
12048 * in order for a value to be mapped.
12050 valueField: undefined,
12052 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12057 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12058 * field's data value (defaults to the underlying DOM element's name)
12060 hiddenName: undefined,
12062 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12066 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12068 selectedClass: 'active',
12071 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12075 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12076 * anchor positions (defaults to 'tl-bl')
12078 listAlign: 'tl-bl?',
12080 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12084 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12085 * query specified by the allQuery config option (defaults to 'query')
12087 triggerAction: 'query',
12089 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12090 * (defaults to 4, does not apply if editable = false)
12094 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12095 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12099 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12100 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12104 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12105 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12109 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12110 * when editable = true (defaults to false)
12112 selectOnFocus:false,
12114 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12116 queryParam: 'query',
12118 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12119 * when mode = 'remote' (defaults to 'Loading...')
12121 loadingText: 'Loading...',
12123 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12127 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12131 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12132 * traditional select (defaults to true)
12136 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12140 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12144 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12145 * listWidth has a higher value)
12149 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12150 * allow the user to set arbitrary text into the field (defaults to false)
12152 forceSelection:false,
12154 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12155 * if typeAhead = true (defaults to 250)
12157 typeAheadDelay : 250,
12159 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12160 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12162 valueNotFoundText : undefined,
12164 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12166 blockFocus : false,
12169 * @cfg {Boolean} disableClear Disable showing of clear button.
12171 disableClear : false,
12173 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12175 alwaysQuery : false,
12178 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12183 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12185 invalidClass : "has-warning",
12188 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12190 validClass : "has-success",
12193 * @cfg {Boolean} specialFilter (true|false) special filter default false
12195 specialFilter : false,
12198 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12200 mobileTouchView : true,
12212 btnPosition : 'right',
12213 triggerList : true,
12214 showToggleBtn : true,
12216 emptyResultText: 'Empty',
12217 triggerText : 'Select',
12219 // element that contains real text value.. (when hidden is used..)
12221 getAutoCreate : function()
12229 if(Roo.isTouch && this.mobileTouchView){
12230 cfg = this.getAutoCreateTouchView();
12237 if(!this.tickable){
12238 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12243 * ComboBox with tickable selections
12246 var align = this.labelAlign || this.parentLabelAlign();
12249 cls : 'form-group roo-combobox-tickable' //input-group
12254 cls : 'tickable-buttons',
12259 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12260 html : this.triggerText
12266 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12273 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12280 buttons.cn.unshift({
12282 cls: 'roo-select2-search-field-input'
12288 Roo.each(buttons.cn, function(c){
12290 c.cls += ' btn-' + _this.size;
12293 if (_this.disabled) {
12304 cls: 'form-hidden-field'
12308 cls: 'roo-select2-choices',
12312 cls: 'roo-select2-search-field',
12324 cls: 'roo-select2-container input-group roo-select2-container-multi',
12329 // cls: 'typeahead typeahead-long dropdown-menu',
12330 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12335 if(this.hasFeedback && !this.allowBlank){
12339 cls: 'glyphicon form-control-feedback'
12342 combobox.cn.push(feedback);
12345 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12347 // Roo.log("left and has label");
12351 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12352 tooltip : 'This field is required'
12357 cls : 'control-label col-sm-' + this.labelWidth,
12358 html : this.fieldLabel
12362 cls : "col-sm-" + (12 - this.labelWidth),
12370 if(this.indicatorpos == 'right'){
12376 cls : 'control-label col-sm-' + this.labelWidth,
12377 html : this.fieldLabel
12382 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12383 tooltip : 'This field is required'
12386 cls : "col-sm-" + (12 - this.labelWidth),
12397 } else if ( this.fieldLabel.length) {
12398 // Roo.log(" label");
12402 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12403 tooltip : 'This field is required'
12407 //cls : 'input-group-addon',
12408 html : this.fieldLabel
12416 if(this.indicatorpos == 'right'){
12421 //cls : 'input-group-addon',
12422 html : this.fieldLabel
12428 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12429 tooltip : 'This field is required'
12440 // Roo.log(" no label && no align");
12447 ['xs','sm','md','lg'].map(function(size){
12448 if (settings[size]) {
12449 cfg.cls += ' col-' + size + '-' + settings[size];
12457 _initEventsCalled : false,
12460 initEvents: function()
12463 if (this._initEventsCalled) { // as we call render... prevent looping...
12466 this._initEventsCalled = true;
12469 throw "can not find store for combo";
12472 this.store = Roo.factory(this.store, Roo.data);
12474 // if we are building from html. then this element is so complex, that we can not really
12475 // use the rendered HTML.
12476 // so we have to trash and replace the previous code.
12477 if (Roo.XComponent.build_from_html) {
12479 // remove this element....
12480 var e = this.el.dom, k=0;
12481 while (e ) { e = e.previousSibling; ++k;}
12486 this.rendered = false;
12488 this.render(this.parent().getChildContainer(true), k);
12499 if(Roo.isTouch && this.mobileTouchView){
12500 this.initTouchView();
12505 this.initTickableEvents();
12509 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12511 if(this.hiddenName){
12513 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12515 this.hiddenField.dom.value =
12516 this.hiddenValue !== undefined ? this.hiddenValue :
12517 this.value !== undefined ? this.value : '';
12519 // prevent input submission
12520 this.el.dom.removeAttribute('name');
12521 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12526 // this.el.dom.setAttribute('autocomplete', 'off');
12529 var cls = 'x-combo-list';
12531 //this.list = new Roo.Layer({
12532 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12538 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12539 _this.list.setWidth(lw);
12542 this.list.on('mouseover', this.onViewOver, this);
12543 this.list.on('mousemove', this.onViewMove, this);
12545 this.list.on('scroll', this.onViewScroll, this);
12548 this.list.swallowEvent('mousewheel');
12549 this.assetHeight = 0;
12552 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12553 this.assetHeight += this.header.getHeight();
12556 this.innerList = this.list.createChild({cls:cls+'-inner'});
12557 this.innerList.on('mouseover', this.onViewOver, this);
12558 this.innerList.on('mousemove', this.onViewMove, this);
12559 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12561 if(this.allowBlank && !this.pageSize && !this.disableClear){
12562 this.footer = this.list.createChild({cls:cls+'-ft'});
12563 this.pageTb = new Roo.Toolbar(this.footer);
12567 this.footer = this.list.createChild({cls:cls+'-ft'});
12568 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12569 {pageSize: this.pageSize});
12573 if (this.pageTb && this.allowBlank && !this.disableClear) {
12575 this.pageTb.add(new Roo.Toolbar.Fill(), {
12576 cls: 'x-btn-icon x-btn-clear',
12578 handler: function()
12581 _this.clearValue();
12582 _this.onSelect(false, -1);
12587 this.assetHeight += this.footer.getHeight();
12592 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12595 this.view = new Roo.View(this.list, this.tpl, {
12596 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12598 //this.view.wrapEl.setDisplayed(false);
12599 this.view.on('click', this.onViewClick, this);
12603 this.store.on('beforeload', this.onBeforeLoad, this);
12604 this.store.on('load', this.onLoad, this);
12605 this.store.on('loadexception', this.onLoadException, this);
12607 if(this.resizable){
12608 this.resizer = new Roo.Resizable(this.list, {
12609 pinned:true, handles:'se'
12611 this.resizer.on('resize', function(r, w, h){
12612 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12613 this.listWidth = w;
12614 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12615 this.restrictHeight();
12617 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12620 if(!this.editable){
12621 this.editable = true;
12622 this.setEditable(false);
12627 if (typeof(this.events.add.listeners) != 'undefined') {
12629 this.addicon = this.wrap.createChild(
12630 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12632 this.addicon.on('click', function(e) {
12633 this.fireEvent('add', this);
12636 if (typeof(this.events.edit.listeners) != 'undefined') {
12638 this.editicon = this.wrap.createChild(
12639 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12640 if (this.addicon) {
12641 this.editicon.setStyle('margin-left', '40px');
12643 this.editicon.on('click', function(e) {
12645 // we fire even if inothing is selected..
12646 this.fireEvent('edit', this, this.lastData );
12652 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12653 "up" : function(e){
12654 this.inKeyMode = true;
12658 "down" : function(e){
12659 if(!this.isExpanded()){
12660 this.onTriggerClick();
12662 this.inKeyMode = true;
12667 "enter" : function(e){
12668 // this.onViewClick();
12672 if(this.fireEvent("specialkey", this, e)){
12673 this.onViewClick(false);
12679 "esc" : function(e){
12683 "tab" : function(e){
12686 if(this.fireEvent("specialkey", this, e)){
12687 this.onViewClick(false);
12695 doRelay : function(foo, bar, hname){
12696 if(hname == 'down' || this.scope.isExpanded()){
12697 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12706 this.queryDelay = Math.max(this.queryDelay || 10,
12707 this.mode == 'local' ? 10 : 250);
12710 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12712 if(this.typeAhead){
12713 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12715 if(this.editable !== false){
12716 this.inputEl().on("keyup", this.onKeyUp, this);
12718 if(this.forceSelection){
12719 this.inputEl().on('blur', this.doForce, this);
12723 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12724 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12728 initTickableEvents: function()
12732 if(this.hiddenName){
12734 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12736 this.hiddenField.dom.value =
12737 this.hiddenValue !== undefined ? this.hiddenValue :
12738 this.value !== undefined ? this.value : '';
12740 // prevent input submission
12741 this.el.dom.removeAttribute('name');
12742 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12747 // this.list = this.el.select('ul.dropdown-menu',true).first();
12749 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12750 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12751 if(this.triggerList){
12752 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12755 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12756 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12758 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12759 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12761 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12762 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12764 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12765 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12766 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12769 this.cancelBtn.hide();
12774 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12775 _this.list.setWidth(lw);
12778 this.list.on('mouseover', this.onViewOver, this);
12779 this.list.on('mousemove', this.onViewMove, this);
12781 this.list.on('scroll', this.onViewScroll, this);
12784 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>';
12787 this.view = new Roo.View(this.list, this.tpl, {
12788 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12791 //this.view.wrapEl.setDisplayed(false);
12792 this.view.on('click', this.onViewClick, this);
12796 this.store.on('beforeload', this.onBeforeLoad, this);
12797 this.store.on('load', this.onLoad, this);
12798 this.store.on('loadexception', this.onLoadException, this);
12801 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12802 "up" : function(e){
12803 this.inKeyMode = true;
12807 "down" : function(e){
12808 this.inKeyMode = true;
12812 "enter" : function(e){
12813 if(this.fireEvent("specialkey", this, e)){
12814 this.onViewClick(false);
12820 "esc" : function(e){
12821 this.onTickableFooterButtonClick(e, false, false);
12824 "tab" : function(e){
12825 this.fireEvent("specialkey", this, e);
12827 this.onTickableFooterButtonClick(e, false, false);
12834 doRelay : function(e, fn, key){
12835 if(this.scope.isExpanded()){
12836 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12845 this.queryDelay = Math.max(this.queryDelay || 10,
12846 this.mode == 'local' ? 10 : 250);
12849 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12851 if(this.typeAhead){
12852 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12855 if(this.editable !== false){
12856 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12861 onDestroy : function(){
12863 this.view.setStore(null);
12864 this.view.el.removeAllListeners();
12865 this.view.el.remove();
12866 this.view.purgeListeners();
12869 this.list.dom.innerHTML = '';
12873 this.store.un('beforeload', this.onBeforeLoad, this);
12874 this.store.un('load', this.onLoad, this);
12875 this.store.un('loadexception', this.onLoadException, this);
12877 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12881 fireKey : function(e){
12882 if(e.isNavKeyPress() && !this.list.isVisible()){
12883 this.fireEvent("specialkey", this, e);
12888 onResize: function(w, h){
12889 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12891 // if(typeof w != 'number'){
12892 // // we do not handle it!?!?
12895 // var tw = this.trigger.getWidth();
12896 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12897 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12899 // this.inputEl().setWidth( this.adjustWidth('input', x));
12901 // //this.trigger.setStyle('left', x+'px');
12903 // if(this.list && this.listWidth === undefined){
12904 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12905 // this.list.setWidth(lw);
12906 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12914 * Allow or prevent the user from directly editing the field text. If false is passed,
12915 * the user will only be able to select from the items defined in the dropdown list. This method
12916 * is the runtime equivalent of setting the 'editable' config option at config time.
12917 * @param {Boolean} value True to allow the user to directly edit the field text
12919 setEditable : function(value){
12920 if(value == this.editable){
12923 this.editable = value;
12925 this.inputEl().dom.setAttribute('readOnly', true);
12926 this.inputEl().on('mousedown', this.onTriggerClick, this);
12927 this.inputEl().addClass('x-combo-noedit');
12929 this.inputEl().dom.setAttribute('readOnly', false);
12930 this.inputEl().un('mousedown', this.onTriggerClick, this);
12931 this.inputEl().removeClass('x-combo-noedit');
12937 onBeforeLoad : function(combo,opts){
12938 if(!this.hasFocus){
12942 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12944 this.restrictHeight();
12945 this.selectedIndex = -1;
12949 onLoad : function(){
12951 this.hasQuery = false;
12953 if(!this.hasFocus){
12957 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12958 this.loading.hide();
12961 if(this.store.getCount() > 0){
12963 this.restrictHeight();
12964 if(this.lastQuery == this.allQuery){
12965 if(this.editable && !this.tickable){
12966 this.inputEl().dom.select();
12970 !this.selectByValue(this.value, true) &&
12973 !this.store.lastOptions ||
12974 typeof(this.store.lastOptions.add) == 'undefined' ||
12975 this.store.lastOptions.add != true
12978 this.select(0, true);
12981 if(this.autoFocus){
12984 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12985 this.taTask.delay(this.typeAheadDelay);
12989 this.onEmptyResults();
12995 onLoadException : function()
12997 this.hasQuery = false;
12999 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13000 this.loading.hide();
13003 if(this.tickable && this.editable){
13008 // only causes errors at present
13009 //Roo.log(this.store.reader.jsonData);
13010 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13012 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13018 onTypeAhead : function(){
13019 if(this.store.getCount() > 0){
13020 var r = this.store.getAt(0);
13021 var newValue = r.data[this.displayField];
13022 var len = newValue.length;
13023 var selStart = this.getRawValue().length;
13025 if(selStart != len){
13026 this.setRawValue(newValue);
13027 this.selectText(selStart, newValue.length);
13033 onSelect : function(record, index){
13035 if(this.fireEvent('beforeselect', this, record, index) !== false){
13037 this.setFromData(index > -1 ? record.data : false);
13040 this.fireEvent('select', this, record, index);
13045 * Returns the currently selected field value or empty string if no value is set.
13046 * @return {String} value The selected value
13048 getValue : function(){
13051 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13054 if(this.valueField){
13055 return typeof this.value != 'undefined' ? this.value : '';
13057 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13062 * Clears any text/value currently set in the field
13064 clearValue : function(){
13065 if(this.hiddenField){
13066 this.hiddenField.dom.value = '';
13069 this.setRawValue('');
13070 this.lastSelectionText = '';
13071 this.lastData = false;
13073 var close = this.closeTriggerEl();
13084 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13085 * will be displayed in the field. If the value does not match the data value of an existing item,
13086 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13087 * Otherwise the field will be blank (although the value will still be set).
13088 * @param {String} value The value to match
13090 setValue : function(v){
13097 if(this.valueField){
13098 var r = this.findRecord(this.valueField, v);
13100 text = r.data[this.displayField];
13101 }else if(this.valueNotFoundText !== undefined){
13102 text = this.valueNotFoundText;
13105 this.lastSelectionText = text;
13106 if(this.hiddenField){
13107 this.hiddenField.dom.value = v;
13109 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13112 var close = this.closeTriggerEl();
13115 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13121 * @property {Object} the last set data for the element
13126 * Sets the value of the field based on a object which is related to the record format for the store.
13127 * @param {Object} value the value to set as. or false on reset?
13129 setFromData : function(o){
13136 var dv = ''; // display value
13137 var vv = ''; // value value..
13139 if (this.displayField) {
13140 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13142 // this is an error condition!!!
13143 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13146 if(this.valueField){
13147 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13150 var close = this.closeTriggerEl();
13153 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13156 if(this.hiddenField){
13157 this.hiddenField.dom.value = vv;
13159 this.lastSelectionText = dv;
13160 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13164 // no hidden field.. - we store the value in 'value', but still display
13165 // display field!!!!
13166 this.lastSelectionText = dv;
13167 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13174 reset : function(){
13175 // overridden so that last data is reset..
13182 this.setValue(this.originalValue);
13183 //this.clearInvalid();
13184 this.lastData = false;
13186 this.view.clearSelections();
13192 findRecord : function(prop, value){
13194 if(this.store.getCount() > 0){
13195 this.store.each(function(r){
13196 if(r.data[prop] == value){
13206 getName: function()
13208 // returns hidden if it's set..
13209 if (!this.rendered) {return ''};
13210 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13214 onViewMove : function(e, t){
13215 this.inKeyMode = false;
13219 onViewOver : function(e, t){
13220 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13223 var item = this.view.findItemFromChild(t);
13226 var index = this.view.indexOf(item);
13227 this.select(index, false);
13232 onViewClick : function(view, doFocus, el, e)
13234 var index = this.view.getSelectedIndexes()[0];
13236 var r = this.store.getAt(index);
13240 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13247 Roo.each(this.tickItems, function(v,k){
13249 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13251 _this.tickItems.splice(k, 1);
13253 if(typeof(e) == 'undefined' && view == false){
13254 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13266 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13267 this.tickItems.push(r.data);
13270 if(typeof(e) == 'undefined' && view == false){
13271 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13278 this.onSelect(r, index);
13280 if(doFocus !== false && !this.blockFocus){
13281 this.inputEl().focus();
13286 restrictHeight : function(){
13287 //this.innerList.dom.style.height = '';
13288 //var inner = this.innerList.dom;
13289 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13290 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13291 //this.list.beginUpdate();
13292 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13293 this.list.alignTo(this.inputEl(), this.listAlign);
13294 this.list.alignTo(this.inputEl(), this.listAlign);
13295 //this.list.endUpdate();
13299 onEmptyResults : function(){
13301 if(this.tickable && this.editable){
13302 this.restrictHeight();
13310 * Returns true if the dropdown list is expanded, else false.
13312 isExpanded : function(){
13313 return this.list.isVisible();
13317 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13318 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13319 * @param {String} value The data value of the item to select
13320 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13321 * selected item if it is not currently in view (defaults to true)
13322 * @return {Boolean} True if the value matched an item in the list, else false
13324 selectByValue : function(v, scrollIntoView){
13325 if(v !== undefined && v !== null){
13326 var r = this.findRecord(this.valueField || this.displayField, v);
13328 this.select(this.store.indexOf(r), scrollIntoView);
13336 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13337 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13338 * @param {Number} index The zero-based index of the list item to select
13339 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13340 * selected item if it is not currently in view (defaults to true)
13342 select : function(index, scrollIntoView){
13343 this.selectedIndex = index;
13344 this.view.select(index);
13345 if(scrollIntoView !== false){
13346 var el = this.view.getNode(index);
13348 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13351 this.list.scrollChildIntoView(el, false);
13357 selectNext : function(){
13358 var ct = this.store.getCount();
13360 if(this.selectedIndex == -1){
13362 }else if(this.selectedIndex < ct-1){
13363 this.select(this.selectedIndex+1);
13369 selectPrev : function(){
13370 var ct = this.store.getCount();
13372 if(this.selectedIndex == -1){
13374 }else if(this.selectedIndex != 0){
13375 this.select(this.selectedIndex-1);
13381 onKeyUp : function(e){
13382 if(this.editable !== false && !e.isSpecialKey()){
13383 this.lastKey = e.getKey();
13384 this.dqTask.delay(this.queryDelay);
13389 validateBlur : function(){
13390 return !this.list || !this.list.isVisible();
13394 initQuery : function(){
13396 var v = this.getRawValue();
13398 if(this.tickable && this.editable){
13399 v = this.tickableInputEl().getValue();
13406 doForce : function(){
13407 if(this.inputEl().dom.value.length > 0){
13408 this.inputEl().dom.value =
13409 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13415 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13416 * query allowing the query action to be canceled if needed.
13417 * @param {String} query The SQL query to execute
13418 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13419 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13420 * saved in the current store (defaults to false)
13422 doQuery : function(q, forceAll){
13424 if(q === undefined || q === null){
13429 forceAll: forceAll,
13433 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13438 forceAll = qe.forceAll;
13439 if(forceAll === true || (q.length >= this.minChars)){
13441 this.hasQuery = true;
13443 if(this.lastQuery != q || this.alwaysQuery){
13444 this.lastQuery = q;
13445 if(this.mode == 'local'){
13446 this.selectedIndex = -1;
13448 this.store.clearFilter();
13451 if(this.specialFilter){
13452 this.fireEvent('specialfilter', this);
13457 this.store.filter(this.displayField, q);
13460 this.store.fireEvent("datachanged", this.store);
13467 this.store.baseParams[this.queryParam] = q;
13469 var options = {params : this.getParams(q)};
13472 options.add = true;
13473 options.params.start = this.page * this.pageSize;
13476 this.store.load(options);
13479 * this code will make the page width larger, at the beginning, the list not align correctly,
13480 * we should expand the list on onLoad
13481 * so command out it
13486 this.selectedIndex = -1;
13491 this.loadNext = false;
13495 getParams : function(q){
13497 //p[this.queryParam] = q;
13501 p.limit = this.pageSize;
13507 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13509 collapse : function(){
13510 if(!this.isExpanded()){
13517 this.hasFocus = false;
13519 this.cancelBtn.hide();
13520 this.trigger.show();
13523 this.tickableInputEl().dom.value = '';
13524 this.tickableInputEl().blur();
13529 Roo.get(document).un('mousedown', this.collapseIf, this);
13530 Roo.get(document).un('mousewheel', this.collapseIf, this);
13531 if (!this.editable) {
13532 Roo.get(document).un('keydown', this.listKeyPress, this);
13534 this.fireEvent('collapse', this);
13540 collapseIf : function(e){
13541 var in_combo = e.within(this.el);
13542 var in_list = e.within(this.list);
13543 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13545 if (in_combo || in_list || is_list) {
13546 //e.stopPropagation();
13551 this.onTickableFooterButtonClick(e, false, false);
13559 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13561 expand : function(){
13563 if(this.isExpanded() || !this.hasFocus){
13567 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13568 this.list.setWidth(lw);
13575 this.restrictHeight();
13579 this.tickItems = Roo.apply([], this.item);
13582 this.cancelBtn.show();
13583 this.trigger.hide();
13586 this.tickableInputEl().focus();
13591 Roo.get(document).on('mousedown', this.collapseIf, this);
13592 Roo.get(document).on('mousewheel', this.collapseIf, this);
13593 if (!this.editable) {
13594 Roo.get(document).on('keydown', this.listKeyPress, this);
13597 this.fireEvent('expand', this);
13601 // Implements the default empty TriggerField.onTriggerClick function
13602 onTriggerClick : function(e)
13604 Roo.log('trigger click');
13606 if(this.disabled || !this.triggerList){
13611 this.loadNext = false;
13613 if(this.isExpanded()){
13615 if (!this.blockFocus) {
13616 this.inputEl().focus();
13620 this.hasFocus = true;
13621 if(this.triggerAction == 'all') {
13622 this.doQuery(this.allQuery, true);
13624 this.doQuery(this.getRawValue());
13626 if (!this.blockFocus) {
13627 this.inputEl().focus();
13632 onTickableTriggerClick : function(e)
13639 this.loadNext = false;
13640 this.hasFocus = true;
13642 if(this.triggerAction == 'all') {
13643 this.doQuery(this.allQuery, true);
13645 this.doQuery(this.getRawValue());
13649 onSearchFieldClick : function(e)
13651 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13652 this.onTickableFooterButtonClick(e, false, false);
13656 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13661 this.loadNext = false;
13662 this.hasFocus = true;
13664 if(this.triggerAction == 'all') {
13665 this.doQuery(this.allQuery, true);
13667 this.doQuery(this.getRawValue());
13671 listKeyPress : function(e)
13673 //Roo.log('listkeypress');
13674 // scroll to first matching element based on key pres..
13675 if (e.isSpecialKey()) {
13678 var k = String.fromCharCode(e.getKey()).toUpperCase();
13681 var csel = this.view.getSelectedNodes();
13682 var cselitem = false;
13684 var ix = this.view.indexOf(csel[0]);
13685 cselitem = this.store.getAt(ix);
13686 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13692 this.store.each(function(v) {
13694 // start at existing selection.
13695 if (cselitem.id == v.id) {
13701 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13702 match = this.store.indexOf(v);
13708 if (match === false) {
13709 return true; // no more action?
13712 this.view.select(match);
13713 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13714 sn.scrollIntoView(sn.dom.parentNode, false);
13717 onViewScroll : function(e, t){
13719 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){
13723 this.hasQuery = true;
13725 this.loading = this.list.select('.loading', true).first();
13727 if(this.loading === null){
13728 this.list.createChild({
13730 cls: 'loading roo-select2-more-results roo-select2-active',
13731 html: 'Loading more results...'
13734 this.loading = this.list.select('.loading', true).first();
13736 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13738 this.loading.hide();
13741 this.loading.show();
13746 this.loadNext = true;
13748 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13753 addItem : function(o)
13755 var dv = ''; // display value
13757 if (this.displayField) {
13758 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13760 // this is an error condition!!!
13761 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13768 var choice = this.choices.createChild({
13770 cls: 'roo-select2-search-choice',
13779 cls: 'roo-select2-search-choice-close',
13784 }, this.searchField);
13786 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13788 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13796 this.inputEl().dom.value = '';
13801 onRemoveItem : function(e, _self, o)
13803 e.preventDefault();
13805 this.lastItem = Roo.apply([], this.item);
13807 var index = this.item.indexOf(o.data) * 1;
13810 Roo.log('not this item?!');
13814 this.item.splice(index, 1);
13819 this.fireEvent('remove', this, e);
13825 syncValue : function()
13827 if(!this.item.length){
13834 Roo.each(this.item, function(i){
13835 if(_this.valueField){
13836 value.push(i[_this.valueField]);
13843 this.value = value.join(',');
13845 if(this.hiddenField){
13846 this.hiddenField.dom.value = this.value;
13849 this.store.fireEvent("datachanged", this.store);
13854 clearItem : function()
13856 if(!this.multiple){
13862 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13870 if(this.tickable && !Roo.isTouch){
13871 this.view.refresh();
13875 inputEl: function ()
13877 if(Roo.isTouch && this.mobileTouchView){
13878 return this.el.select('input.form-control',true).first();
13882 return this.searchField;
13885 return this.el.select('input.form-control',true).first();
13889 onTickableFooterButtonClick : function(e, btn, el)
13891 e.preventDefault();
13893 this.lastItem = Roo.apply([], this.item);
13895 if(btn && btn.name == 'cancel'){
13896 this.tickItems = Roo.apply([], this.item);
13905 Roo.each(this.tickItems, function(o){
13913 validate : function()
13915 var v = this.getRawValue();
13918 v = this.getValue();
13921 if(this.disabled || this.allowBlank || v.length){
13926 this.markInvalid();
13930 tickableInputEl : function()
13932 if(!this.tickable || !this.editable){
13933 return this.inputEl();
13936 return this.inputEl().select('.roo-select2-search-field-input', true).first();
13940 getAutoCreateTouchView : function()
13945 cls: 'form-group' //input-group
13951 type : this.inputType,
13952 cls : 'form-control x-combo-noedit',
13953 autocomplete: 'new-password',
13954 placeholder : this.placeholder || '',
13959 input.name = this.name;
13963 input.cls += ' input-' + this.size;
13966 if (this.disabled) {
13967 input.disabled = true;
13978 inputblock.cls += ' input-group';
13980 inputblock.cn.unshift({
13982 cls : 'input-group-addon',
13987 if(this.removable && !this.multiple){
13988 inputblock.cls += ' roo-removable';
13990 inputblock.cn.push({
13993 cls : 'roo-combo-removable-btn close'
13997 if(this.hasFeedback && !this.allowBlank){
13999 inputblock.cls += ' has-feedback';
14001 inputblock.cn.push({
14003 cls: 'glyphicon form-control-feedback'
14010 inputblock.cls += (this.before) ? '' : ' input-group';
14012 inputblock.cn.push({
14014 cls : 'input-group-addon',
14025 cls: 'form-hidden-field'
14039 cls: 'form-hidden-field'
14043 cls: 'roo-select2-choices',
14047 cls: 'roo-select2-search-field',
14060 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14066 if(!this.multiple && this.showToggleBtn){
14073 if (this.caret != false) {
14076 cls: 'fa fa-' + this.caret
14083 cls : 'input-group-addon btn dropdown-toggle',
14088 cls: 'combobox-clear',
14102 combobox.cls += ' roo-select2-container-multi';
14105 var align = this.labelAlign || this.parentLabelAlign();
14109 if(this.fieldLabel.length && this.labelWidth){
14111 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14112 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14117 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14118 tooltip : 'This field is required'
14122 cls : 'control-label ' + lw,
14123 html : this.fieldLabel
14134 if(this.indicatorpos == 'right'){
14138 cls : 'control-label ' + lw,
14139 html : this.fieldLabel
14144 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14145 tooltip : 'This field is required'
14157 var settings = this;
14159 ['xs','sm','md','lg'].map(function(size){
14160 if (settings[size]) {
14161 cfg.cls += ' col-' + size + '-' + settings[size];
14168 initTouchView : function()
14170 this.renderTouchView();
14172 this.touchViewEl.on('scroll', function(){
14173 this.el.dom.scrollTop = 0;
14176 this.originalValue = this.getValue();
14178 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14180 this.inputEl().on("click", this.showTouchView, this);
14181 this.triggerEl.on("click", this.showTouchView, this);
14183 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14184 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14186 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14188 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14189 this.store.on('load', this.onTouchViewLoad, this);
14190 this.store.on('loadexception', this.onTouchViewLoadException, this);
14192 if(this.hiddenName){
14194 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14196 this.hiddenField.dom.value =
14197 this.hiddenValue !== undefined ? this.hiddenValue :
14198 this.value !== undefined ? this.value : '';
14200 this.el.dom.removeAttribute('name');
14201 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14205 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14206 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14209 if(this.removable && !this.multiple){
14210 var close = this.closeTriggerEl();
14212 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14213 close.on('click', this.removeBtnClick, this, close);
14217 * fix the bug in Safari iOS8
14219 this.inputEl().on("focus", function(e){
14220 document.activeElement.blur();
14228 renderTouchView : function()
14230 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14231 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14233 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14234 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14236 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14237 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14238 this.touchViewBodyEl.setStyle('overflow', 'auto');
14240 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14241 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14243 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14244 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14248 showTouchView : function()
14254 this.touchViewHeaderEl.hide();
14256 if(this.modalTitle.length){
14257 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14258 this.touchViewHeaderEl.show();
14261 this.touchViewEl.show();
14263 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14264 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14265 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14267 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14269 if(this.modalTitle.length){
14270 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14273 this.touchViewBodyEl.setHeight(bodyHeight);
14277 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14279 this.touchViewEl.addClass('in');
14282 this.doTouchViewQuery();
14286 hideTouchView : function()
14288 this.touchViewEl.removeClass('in');
14292 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14294 this.touchViewEl.setStyle('display', 'none');
14299 setTouchViewValue : function()
14306 Roo.each(this.tickItems, function(o){
14311 this.hideTouchView();
14314 doTouchViewQuery : function()
14323 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14327 if(!this.alwaysQuery || this.mode == 'local'){
14328 this.onTouchViewLoad();
14335 onTouchViewBeforeLoad : function(combo,opts)
14341 onTouchViewLoad : function()
14343 if(this.store.getCount() < 1){
14344 this.onTouchViewEmptyResults();
14348 this.clearTouchView();
14350 var rawValue = this.getRawValue();
14352 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14354 this.tickItems = [];
14356 this.store.data.each(function(d, rowIndex){
14357 var row = this.touchViewListGroup.createChild(template);
14359 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14360 row.addClass(d.data.cls);
14363 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14366 html : d.data[this.displayField]
14369 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14370 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14373 row.removeClass('selected');
14374 if(!this.multiple && this.valueField &&
14375 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14378 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14379 row.addClass('selected');
14382 if(this.multiple && this.valueField &&
14383 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14387 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14388 this.tickItems.push(d.data);
14391 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14395 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14397 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14399 if(this.modalTitle.length){
14400 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14403 var listHeight = this.touchViewListGroup.getHeight();
14407 if(firstChecked && listHeight > bodyHeight){
14408 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14413 onTouchViewLoadException : function()
14415 this.hideTouchView();
14418 onTouchViewEmptyResults : function()
14420 this.clearTouchView();
14422 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14424 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14428 clearTouchView : function()
14430 this.touchViewListGroup.dom.innerHTML = '';
14433 onTouchViewClick : function(e, el, o)
14435 e.preventDefault();
14438 var rowIndex = o.rowIndex;
14440 var r = this.store.getAt(rowIndex);
14442 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14444 if(!this.multiple){
14445 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14446 c.dom.removeAttribute('checked');
14449 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14451 this.setFromData(r.data);
14453 var close = this.closeTriggerEl();
14459 this.hideTouchView();
14461 this.fireEvent('select', this, r, rowIndex);
14466 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14467 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14468 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14472 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14473 this.addItem(r.data);
14474 this.tickItems.push(r.data);
14480 * @cfg {Boolean} grow
14484 * @cfg {Number} growMin
14488 * @cfg {Number} growMax
14497 Roo.apply(Roo.bootstrap.ComboBox, {
14501 cls: 'modal-header',
14523 cls: 'list-group-item',
14527 cls: 'roo-combobox-list-group-item-value'
14531 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14545 listItemCheckbox : {
14547 cls: 'list-group-item',
14551 cls: 'roo-combobox-list-group-item-value'
14555 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14571 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14576 cls: 'modal-footer',
14584 cls: 'col-xs-6 text-left',
14587 cls: 'btn btn-danger roo-touch-view-cancel',
14593 cls: 'col-xs-6 text-right',
14596 cls: 'btn btn-success roo-touch-view-ok',
14607 Roo.apply(Roo.bootstrap.ComboBox, {
14609 touchViewTemplate : {
14611 cls: 'modal fade roo-combobox-touch-view',
14615 cls: 'modal-dialog',
14616 style : 'position:fixed', // we have to fix position....
14620 cls: 'modal-content',
14622 Roo.bootstrap.ComboBox.header,
14623 Roo.bootstrap.ComboBox.body,
14624 Roo.bootstrap.ComboBox.footer
14633 * Ext JS Library 1.1.1
14634 * Copyright(c) 2006-2007, Ext JS, LLC.
14636 * Originally Released Under LGPL - original licence link has changed is not relivant.
14639 * <script type="text/javascript">
14644 * @extends Roo.util.Observable
14645 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14646 * This class also supports single and multi selection modes. <br>
14647 * Create a data model bound view:
14649 var store = new Roo.data.Store(...);
14651 var view = new Roo.View({
14653 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14655 singleSelect: true,
14656 selectedClass: "ydataview-selected",
14660 // listen for node click?
14661 view.on("click", function(vw, index, node, e){
14662 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14666 dataModel.load("foobar.xml");
14668 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14670 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14671 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14673 * Note: old style constructor is still suported (container, template, config)
14676 * Create a new View
14677 * @param {Object} config The config object
14680 Roo.View = function(config, depreciated_tpl, depreciated_config){
14682 this.parent = false;
14684 if (typeof(depreciated_tpl) == 'undefined') {
14685 // new way.. - universal constructor.
14686 Roo.apply(this, config);
14687 this.el = Roo.get(this.el);
14690 this.el = Roo.get(config);
14691 this.tpl = depreciated_tpl;
14692 Roo.apply(this, depreciated_config);
14694 this.wrapEl = this.el.wrap().wrap();
14695 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14698 if(typeof(this.tpl) == "string"){
14699 this.tpl = new Roo.Template(this.tpl);
14701 // support xtype ctors..
14702 this.tpl = new Roo.factory(this.tpl, Roo);
14706 this.tpl.compile();
14711 * @event beforeclick
14712 * Fires before a click is processed. Returns false to cancel the default action.
14713 * @param {Roo.View} this
14714 * @param {Number} index The index of the target node
14715 * @param {HTMLElement} node The target node
14716 * @param {Roo.EventObject} e The raw event object
14718 "beforeclick" : true,
14721 * Fires when a template node is clicked.
14722 * @param {Roo.View} this
14723 * @param {Number} index The index of the target node
14724 * @param {HTMLElement} node The target node
14725 * @param {Roo.EventObject} e The raw event object
14730 * Fires when a template node is double clicked.
14731 * @param {Roo.View} this
14732 * @param {Number} index The index of the target node
14733 * @param {HTMLElement} node The target node
14734 * @param {Roo.EventObject} e The raw event object
14738 * @event contextmenu
14739 * Fires when a template node is right clicked.
14740 * @param {Roo.View} this
14741 * @param {Number} index The index of the target node
14742 * @param {HTMLElement} node The target node
14743 * @param {Roo.EventObject} e The raw event object
14745 "contextmenu" : true,
14747 * @event selectionchange
14748 * Fires when the selected nodes change.
14749 * @param {Roo.View} this
14750 * @param {Array} selections Array of the selected nodes
14752 "selectionchange" : true,
14755 * @event beforeselect
14756 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14757 * @param {Roo.View} this
14758 * @param {HTMLElement} node The node to be selected
14759 * @param {Array} selections Array of currently selected nodes
14761 "beforeselect" : true,
14763 * @event preparedata
14764 * Fires on every row to render, to allow you to change the data.
14765 * @param {Roo.View} this
14766 * @param {Object} data to be rendered (change this)
14768 "preparedata" : true
14776 "click": this.onClick,
14777 "dblclick": this.onDblClick,
14778 "contextmenu": this.onContextMenu,
14782 this.selections = [];
14784 this.cmp = new Roo.CompositeElementLite([]);
14786 this.store = Roo.factory(this.store, Roo.data);
14787 this.setStore(this.store, true);
14790 if ( this.footer && this.footer.xtype) {
14792 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14794 this.footer.dataSource = this.store;
14795 this.footer.container = fctr;
14796 this.footer = Roo.factory(this.footer, Roo);
14797 fctr.insertFirst(this.el);
14799 // this is a bit insane - as the paging toolbar seems to detach the el..
14800 // dom.parentNode.parentNode.parentNode
14801 // they get detached?
14805 Roo.View.superclass.constructor.call(this);
14810 Roo.extend(Roo.View, Roo.util.Observable, {
14813 * @cfg {Roo.data.Store} store Data store to load data from.
14818 * @cfg {String|Roo.Element} el The container element.
14823 * @cfg {String|Roo.Template} tpl The template used by this View
14827 * @cfg {String} dataName the named area of the template to use as the data area
14828 * Works with domtemplates roo-name="name"
14832 * @cfg {String} selectedClass The css class to add to selected nodes
14834 selectedClass : "x-view-selected",
14836 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14841 * @cfg {String} text to display on mask (default Loading)
14845 * @cfg {Boolean} multiSelect Allow multiple selection
14847 multiSelect : false,
14849 * @cfg {Boolean} singleSelect Allow single selection
14851 singleSelect: false,
14854 * @cfg {Boolean} toggleSelect - selecting
14856 toggleSelect : false,
14859 * @cfg {Boolean} tickable - selecting
14864 * Returns the element this view is bound to.
14865 * @return {Roo.Element}
14867 getEl : function(){
14868 return this.wrapEl;
14874 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14876 refresh : function(){
14877 //Roo.log('refresh');
14880 // if we are using something like 'domtemplate', then
14881 // the what gets used is:
14882 // t.applySubtemplate(NAME, data, wrapping data..)
14883 // the outer template then get' applied with
14884 // the store 'extra data'
14885 // and the body get's added to the
14886 // roo-name="data" node?
14887 // <span class='roo-tpl-{name}'></span> ?????
14891 this.clearSelections();
14892 this.el.update("");
14894 var records = this.store.getRange();
14895 if(records.length < 1) {
14897 // is this valid?? = should it render a template??
14899 this.el.update(this.emptyText);
14903 if (this.dataName) {
14904 this.el.update(t.apply(this.store.meta)); //????
14905 el = this.el.child('.roo-tpl-' + this.dataName);
14908 for(var i = 0, len = records.length; i < len; i++){
14909 var data = this.prepareData(records[i].data, i, records[i]);
14910 this.fireEvent("preparedata", this, data, i, records[i]);
14912 var d = Roo.apply({}, data);
14915 Roo.apply(d, {'roo-id' : Roo.id()});
14919 Roo.each(this.parent.item, function(item){
14920 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14923 Roo.apply(d, {'roo-data-checked' : 'checked'});
14927 html[html.length] = Roo.util.Format.trim(
14929 t.applySubtemplate(this.dataName, d, this.store.meta) :
14936 el.update(html.join(""));
14937 this.nodes = el.dom.childNodes;
14938 this.updateIndexes(0);
14943 * Function to override to reformat the data that is sent to
14944 * the template for each node.
14945 * DEPRICATED - use the preparedata event handler.
14946 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14947 * a JSON object for an UpdateManager bound view).
14949 prepareData : function(data, index, record)
14951 this.fireEvent("preparedata", this, data, index, record);
14955 onUpdate : function(ds, record){
14956 // Roo.log('on update');
14957 this.clearSelections();
14958 var index = this.store.indexOf(record);
14959 var n = this.nodes[index];
14960 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14961 n.parentNode.removeChild(n);
14962 this.updateIndexes(index, index);
14968 onAdd : function(ds, records, index)
14970 //Roo.log(['on Add', ds, records, index] );
14971 this.clearSelections();
14972 if(this.nodes.length == 0){
14976 var n = this.nodes[index];
14977 for(var i = 0, len = records.length; i < len; i++){
14978 var d = this.prepareData(records[i].data, i, records[i]);
14980 this.tpl.insertBefore(n, d);
14983 this.tpl.append(this.el, d);
14986 this.updateIndexes(index);
14989 onRemove : function(ds, record, index){
14990 // Roo.log('onRemove');
14991 this.clearSelections();
14992 var el = this.dataName ?
14993 this.el.child('.roo-tpl-' + this.dataName) :
14996 el.dom.removeChild(this.nodes[index]);
14997 this.updateIndexes(index);
15001 * Refresh an individual node.
15002 * @param {Number} index
15004 refreshNode : function(index){
15005 this.onUpdate(this.store, this.store.getAt(index));
15008 updateIndexes : function(startIndex, endIndex){
15009 var ns = this.nodes;
15010 startIndex = startIndex || 0;
15011 endIndex = endIndex || ns.length - 1;
15012 for(var i = startIndex; i <= endIndex; i++){
15013 ns[i].nodeIndex = i;
15018 * Changes the data store this view uses and refresh the view.
15019 * @param {Store} store
15021 setStore : function(store, initial){
15022 if(!initial && this.store){
15023 this.store.un("datachanged", this.refresh);
15024 this.store.un("add", this.onAdd);
15025 this.store.un("remove", this.onRemove);
15026 this.store.un("update", this.onUpdate);
15027 this.store.un("clear", this.refresh);
15028 this.store.un("beforeload", this.onBeforeLoad);
15029 this.store.un("load", this.onLoad);
15030 this.store.un("loadexception", this.onLoad);
15034 store.on("datachanged", this.refresh, this);
15035 store.on("add", this.onAdd, this);
15036 store.on("remove", this.onRemove, this);
15037 store.on("update", this.onUpdate, this);
15038 store.on("clear", this.refresh, this);
15039 store.on("beforeload", this.onBeforeLoad, this);
15040 store.on("load", this.onLoad, this);
15041 store.on("loadexception", this.onLoad, this);
15049 * onbeforeLoad - masks the loading area.
15052 onBeforeLoad : function(store,opts)
15054 //Roo.log('onBeforeLoad');
15056 this.el.update("");
15058 this.el.mask(this.mask ? this.mask : "Loading" );
15060 onLoad : function ()
15067 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15068 * @param {HTMLElement} node
15069 * @return {HTMLElement} The template node
15071 findItemFromChild : function(node){
15072 var el = this.dataName ?
15073 this.el.child('.roo-tpl-' + this.dataName,true) :
15076 if(!node || node.parentNode == el){
15079 var p = node.parentNode;
15080 while(p && p != el){
15081 if(p.parentNode == el){
15090 onClick : function(e){
15091 var item = this.findItemFromChild(e.getTarget());
15093 var index = this.indexOf(item);
15094 if(this.onItemClick(item, index, e) !== false){
15095 this.fireEvent("click", this, index, item, e);
15098 this.clearSelections();
15103 onContextMenu : function(e){
15104 var item = this.findItemFromChild(e.getTarget());
15106 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15111 onDblClick : function(e){
15112 var item = this.findItemFromChild(e.getTarget());
15114 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15118 onItemClick : function(item, index, e)
15120 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15123 if (this.toggleSelect) {
15124 var m = this.isSelected(item) ? 'unselect' : 'select';
15127 _t[m](item, true, false);
15130 if(this.multiSelect || this.singleSelect){
15131 if(this.multiSelect && e.shiftKey && this.lastSelection){
15132 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15134 this.select(item, this.multiSelect && e.ctrlKey);
15135 this.lastSelection = item;
15138 if(!this.tickable){
15139 e.preventDefault();
15147 * Get the number of selected nodes.
15150 getSelectionCount : function(){
15151 return this.selections.length;
15155 * Get the currently selected nodes.
15156 * @return {Array} An array of HTMLElements
15158 getSelectedNodes : function(){
15159 return this.selections;
15163 * Get the indexes of the selected nodes.
15166 getSelectedIndexes : function(){
15167 var indexes = [], s = this.selections;
15168 for(var i = 0, len = s.length; i < len; i++){
15169 indexes.push(s[i].nodeIndex);
15175 * Clear all selections
15176 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15178 clearSelections : function(suppressEvent){
15179 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15180 this.cmp.elements = this.selections;
15181 this.cmp.removeClass(this.selectedClass);
15182 this.selections = [];
15183 if(!suppressEvent){
15184 this.fireEvent("selectionchange", this, this.selections);
15190 * Returns true if the passed node is selected
15191 * @param {HTMLElement/Number} node The node or node index
15192 * @return {Boolean}
15194 isSelected : function(node){
15195 var s = this.selections;
15199 node = this.getNode(node);
15200 return s.indexOf(node) !== -1;
15205 * @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
15206 * @param {Boolean} keepExisting (optional) true to keep existing selections
15207 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15209 select : function(nodeInfo, keepExisting, suppressEvent){
15210 if(nodeInfo instanceof Array){
15212 this.clearSelections(true);
15214 for(var i = 0, len = nodeInfo.length; i < len; i++){
15215 this.select(nodeInfo[i], true, true);
15219 var node = this.getNode(nodeInfo);
15220 if(!node || this.isSelected(node)){
15221 return; // already selected.
15224 this.clearSelections(true);
15227 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15228 Roo.fly(node).addClass(this.selectedClass);
15229 this.selections.push(node);
15230 if(!suppressEvent){
15231 this.fireEvent("selectionchange", this, this.selections);
15239 * @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
15240 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15241 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15243 unselect : function(nodeInfo, keepExisting, suppressEvent)
15245 if(nodeInfo instanceof Array){
15246 Roo.each(this.selections, function(s) {
15247 this.unselect(s, nodeInfo);
15251 var node = this.getNode(nodeInfo);
15252 if(!node || !this.isSelected(node)){
15253 //Roo.log("not selected");
15254 return; // not selected.
15258 Roo.each(this.selections, function(s) {
15260 Roo.fly(node).removeClass(this.selectedClass);
15267 this.selections= ns;
15268 this.fireEvent("selectionchange", this, this.selections);
15272 * Gets a template node.
15273 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15274 * @return {HTMLElement} The node or null if it wasn't found
15276 getNode : function(nodeInfo){
15277 if(typeof nodeInfo == "string"){
15278 return document.getElementById(nodeInfo);
15279 }else if(typeof nodeInfo == "number"){
15280 return this.nodes[nodeInfo];
15286 * Gets a range template nodes.
15287 * @param {Number} startIndex
15288 * @param {Number} endIndex
15289 * @return {Array} An array of nodes
15291 getNodes : function(start, end){
15292 var ns = this.nodes;
15293 start = start || 0;
15294 end = typeof end == "undefined" ? ns.length - 1 : end;
15297 for(var i = start; i <= end; i++){
15301 for(var i = start; i >= end; i--){
15309 * Finds the index of the passed node
15310 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15311 * @return {Number} The index of the node or -1
15313 indexOf : function(node){
15314 node = this.getNode(node);
15315 if(typeof node.nodeIndex == "number"){
15316 return node.nodeIndex;
15318 var ns = this.nodes;
15319 for(var i = 0, len = ns.length; i < len; i++){
15330 * based on jquery fullcalendar
15334 Roo.bootstrap = Roo.bootstrap || {};
15336 * @class Roo.bootstrap.Calendar
15337 * @extends Roo.bootstrap.Component
15338 * Bootstrap Calendar class
15339 * @cfg {Boolean} loadMask (true|false) default false
15340 * @cfg {Object} header generate the user specific header of the calendar, default false
15343 * Create a new Container
15344 * @param {Object} config The config object
15349 Roo.bootstrap.Calendar = function(config){
15350 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15354 * Fires when a date is selected
15355 * @param {DatePicker} this
15356 * @param {Date} date The selected date
15360 * @event monthchange
15361 * Fires when the displayed month changes
15362 * @param {DatePicker} this
15363 * @param {Date} date The selected month
15365 'monthchange': true,
15367 * @event evententer
15368 * Fires when mouse over an event
15369 * @param {Calendar} this
15370 * @param {event} Event
15372 'evententer': true,
15374 * @event eventleave
15375 * Fires when the mouse leaves an
15376 * @param {Calendar} this
15379 'eventleave': true,
15381 * @event eventclick
15382 * Fires when the mouse click an
15383 * @param {Calendar} this
15392 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15395 * @cfg {Number} startDay
15396 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15404 getAutoCreate : function(){
15407 var fc_button = function(name, corner, style, content ) {
15408 return Roo.apply({},{
15410 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15412 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15415 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15426 style : 'width:100%',
15433 cls : 'fc-header-left',
15435 fc_button('prev', 'left', 'arrow', '‹' ),
15436 fc_button('next', 'right', 'arrow', '›' ),
15437 { tag: 'span', cls: 'fc-header-space' },
15438 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15446 cls : 'fc-header-center',
15450 cls: 'fc-header-title',
15453 html : 'month / year'
15461 cls : 'fc-header-right',
15463 /* fc_button('month', 'left', '', 'month' ),
15464 fc_button('week', '', '', 'week' ),
15465 fc_button('day', 'right', '', 'day' )
15477 header = this.header;
15480 var cal_heads = function() {
15482 // fixme - handle this.
15484 for (var i =0; i < Date.dayNames.length; i++) {
15485 var d = Date.dayNames[i];
15488 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15489 html : d.substring(0,3)
15493 ret[0].cls += ' fc-first';
15494 ret[6].cls += ' fc-last';
15497 var cal_cell = function(n) {
15500 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15505 cls: 'fc-day-number',
15509 cls: 'fc-day-content',
15513 style: 'position: relative;' // height: 17px;
15525 var cal_rows = function() {
15528 for (var r = 0; r < 6; r++) {
15535 for (var i =0; i < Date.dayNames.length; i++) {
15536 var d = Date.dayNames[i];
15537 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15540 row.cn[0].cls+=' fc-first';
15541 row.cn[0].cn[0].style = 'min-height:90px';
15542 row.cn[6].cls+=' fc-last';
15546 ret[0].cls += ' fc-first';
15547 ret[4].cls += ' fc-prev-last';
15548 ret[5].cls += ' fc-last';
15555 cls: 'fc-border-separate',
15556 style : 'width:100%',
15564 cls : 'fc-first fc-last',
15582 cls : 'fc-content',
15583 style : "position: relative;",
15586 cls : 'fc-view fc-view-month fc-grid',
15587 style : 'position: relative',
15588 unselectable : 'on',
15591 cls : 'fc-event-container',
15592 style : 'position:absolute;z-index:8;top:0;left:0;'
15610 initEvents : function()
15613 throw "can not find store for calendar";
15619 style: "text-align:center",
15623 style: "background-color:white;width:50%;margin:250 auto",
15627 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15638 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15640 var size = this.el.select('.fc-content', true).first().getSize();
15641 this.maskEl.setSize(size.width, size.height);
15642 this.maskEl.enableDisplayMode("block");
15643 if(!this.loadMask){
15644 this.maskEl.hide();
15647 this.store = Roo.factory(this.store, Roo.data);
15648 this.store.on('load', this.onLoad, this);
15649 this.store.on('beforeload', this.onBeforeLoad, this);
15653 this.cells = this.el.select('.fc-day',true);
15654 //Roo.log(this.cells);
15655 this.textNodes = this.el.query('.fc-day-number');
15656 this.cells.addClassOnOver('fc-state-hover');
15658 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15659 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15660 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15661 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15663 this.on('monthchange', this.onMonthChange, this);
15665 this.update(new Date().clearTime());
15668 resize : function() {
15669 var sz = this.el.getSize();
15671 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15672 this.el.select('.fc-day-content div',true).setHeight(34);
15677 showPrevMonth : function(e){
15678 this.update(this.activeDate.add("mo", -1));
15680 showToday : function(e){
15681 this.update(new Date().clearTime());
15684 showNextMonth : function(e){
15685 this.update(this.activeDate.add("mo", 1));
15689 showPrevYear : function(){
15690 this.update(this.activeDate.add("y", -1));
15694 showNextYear : function(){
15695 this.update(this.activeDate.add("y", 1));
15700 update : function(date)
15702 var vd = this.activeDate;
15703 this.activeDate = date;
15704 // if(vd && this.el){
15705 // var t = date.getTime();
15706 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15707 // Roo.log('using add remove');
15709 // this.fireEvent('monthchange', this, date);
15711 // this.cells.removeClass("fc-state-highlight");
15712 // this.cells.each(function(c){
15713 // if(c.dateValue == t){
15714 // c.addClass("fc-state-highlight");
15715 // setTimeout(function(){
15716 // try{c.dom.firstChild.focus();}catch(e){}
15726 var days = date.getDaysInMonth();
15728 var firstOfMonth = date.getFirstDateOfMonth();
15729 var startingPos = firstOfMonth.getDay()-this.startDay;
15731 if(startingPos < this.startDay){
15735 var pm = date.add(Date.MONTH, -1);
15736 var prevStart = pm.getDaysInMonth()-startingPos;
15738 this.cells = this.el.select('.fc-day',true);
15739 this.textNodes = this.el.query('.fc-day-number');
15740 this.cells.addClassOnOver('fc-state-hover');
15742 var cells = this.cells.elements;
15743 var textEls = this.textNodes;
15745 Roo.each(cells, function(cell){
15746 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15749 days += startingPos;
15751 // convert everything to numbers so it's fast
15752 var day = 86400000;
15753 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15756 //Roo.log(prevStart);
15758 var today = new Date().clearTime().getTime();
15759 var sel = date.clearTime().getTime();
15760 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15761 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15762 var ddMatch = this.disabledDatesRE;
15763 var ddText = this.disabledDatesText;
15764 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15765 var ddaysText = this.disabledDaysText;
15766 var format = this.format;
15768 var setCellClass = function(cal, cell){
15772 //Roo.log('set Cell Class');
15774 var t = d.getTime();
15778 cell.dateValue = t;
15780 cell.className += " fc-today";
15781 cell.className += " fc-state-highlight";
15782 cell.title = cal.todayText;
15785 // disable highlight in other month..
15786 //cell.className += " fc-state-highlight";
15791 cell.className = " fc-state-disabled";
15792 cell.title = cal.minText;
15796 cell.className = " fc-state-disabled";
15797 cell.title = cal.maxText;
15801 if(ddays.indexOf(d.getDay()) != -1){
15802 cell.title = ddaysText;
15803 cell.className = " fc-state-disabled";
15806 if(ddMatch && format){
15807 var fvalue = d.dateFormat(format);
15808 if(ddMatch.test(fvalue)){
15809 cell.title = ddText.replace("%0", fvalue);
15810 cell.className = " fc-state-disabled";
15814 if (!cell.initialClassName) {
15815 cell.initialClassName = cell.dom.className;
15818 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15823 for(; i < startingPos; i++) {
15824 textEls[i].innerHTML = (++prevStart);
15825 d.setDate(d.getDate()+1);
15827 cells[i].className = "fc-past fc-other-month";
15828 setCellClass(this, cells[i]);
15833 for(; i < days; i++){
15834 intDay = i - startingPos + 1;
15835 textEls[i].innerHTML = (intDay);
15836 d.setDate(d.getDate()+1);
15838 cells[i].className = ''; // "x-date-active";
15839 setCellClass(this, cells[i]);
15843 for(; i < 42; i++) {
15844 textEls[i].innerHTML = (++extraDays);
15845 d.setDate(d.getDate()+1);
15847 cells[i].className = "fc-future fc-other-month";
15848 setCellClass(this, cells[i]);
15851 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15853 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15855 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15856 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15858 if(totalRows != 6){
15859 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15860 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15863 this.fireEvent('monthchange', this, date);
15867 if(!this.internalRender){
15868 var main = this.el.dom.firstChild;
15869 var w = main.offsetWidth;
15870 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15871 Roo.fly(main).setWidth(w);
15872 this.internalRender = true;
15873 // opera does not respect the auto grow header center column
15874 // then, after it gets a width opera refuses to recalculate
15875 // without a second pass
15876 if(Roo.isOpera && !this.secondPass){
15877 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15878 this.secondPass = true;
15879 this.update.defer(10, this, [date]);
15886 findCell : function(dt) {
15887 dt = dt.clearTime().getTime();
15889 this.cells.each(function(c){
15890 //Roo.log("check " +c.dateValue + '?=' + dt);
15891 if(c.dateValue == dt){
15901 findCells : function(ev) {
15902 var s = ev.start.clone().clearTime().getTime();
15904 var e= ev.end.clone().clearTime().getTime();
15907 this.cells.each(function(c){
15908 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15910 if(c.dateValue > e){
15913 if(c.dateValue < s){
15922 // findBestRow: function(cells)
15926 // for (var i =0 ; i < cells.length;i++) {
15927 // ret = Math.max(cells[i].rows || 0,ret);
15934 addItem : function(ev)
15936 // look for vertical location slot in
15937 var cells = this.findCells(ev);
15939 // ev.row = this.findBestRow(cells);
15941 // work out the location.
15945 for(var i =0; i < cells.length; i++) {
15947 cells[i].row = cells[0].row;
15950 cells[i].row = cells[i].row + 1;
15960 if (crow.start.getY() == cells[i].getY()) {
15962 crow.end = cells[i];
15979 cells[0].events.push(ev);
15981 this.calevents.push(ev);
15984 clearEvents: function() {
15986 if(!this.calevents){
15990 Roo.each(this.cells.elements, function(c){
15996 Roo.each(this.calevents, function(e) {
15997 Roo.each(e.els, function(el) {
15998 el.un('mouseenter' ,this.onEventEnter, this);
15999 el.un('mouseleave' ,this.onEventLeave, this);
16004 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16010 renderEvents: function()
16014 this.cells.each(function(c) {
16023 if(c.row != c.events.length){
16024 r = 4 - (4 - (c.row - c.events.length));
16027 c.events = ev.slice(0, r);
16028 c.more = ev.slice(r);
16030 if(c.more.length && c.more.length == 1){
16031 c.events.push(c.more.pop());
16034 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16038 this.cells.each(function(c) {
16040 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16043 for (var e = 0; e < c.events.length; e++){
16044 var ev = c.events[e];
16045 var rows = ev.rows;
16047 for(var i = 0; i < rows.length; i++) {
16049 // how many rows should it span..
16052 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16053 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16055 unselectable : "on",
16058 cls: 'fc-event-inner',
16062 // cls: 'fc-event-time',
16063 // html : cells.length > 1 ? '' : ev.time
16067 cls: 'fc-event-title',
16068 html : String.format('{0}', ev.title)
16075 cls: 'ui-resizable-handle ui-resizable-e',
16076 html : '  '
16083 cfg.cls += ' fc-event-start';
16085 if ((i+1) == rows.length) {
16086 cfg.cls += ' fc-event-end';
16089 var ctr = _this.el.select('.fc-event-container',true).first();
16090 var cg = ctr.createChild(cfg);
16092 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16093 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16095 var r = (c.more.length) ? 1 : 0;
16096 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16097 cg.setWidth(ebox.right - sbox.x -2);
16099 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16100 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16101 cg.on('click', _this.onEventClick, _this, ev);
16112 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16113 style : 'position: absolute',
16114 unselectable : "on",
16117 cls: 'fc-event-inner',
16121 cls: 'fc-event-title',
16129 cls: 'ui-resizable-handle ui-resizable-e',
16130 html : '  '
16136 var ctr = _this.el.select('.fc-event-container',true).first();
16137 var cg = ctr.createChild(cfg);
16139 var sbox = c.select('.fc-day-content',true).first().getBox();
16140 var ebox = c.select('.fc-day-content',true).first().getBox();
16142 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16143 cg.setWidth(ebox.right - sbox.x -2);
16145 cg.on('click', _this.onMoreEventClick, _this, c.more);
16155 onEventEnter: function (e, el,event,d) {
16156 this.fireEvent('evententer', this, el, event);
16159 onEventLeave: function (e, el,event,d) {
16160 this.fireEvent('eventleave', this, el, event);
16163 onEventClick: function (e, el,event,d) {
16164 this.fireEvent('eventclick', this, el, event);
16167 onMonthChange: function () {
16171 onMoreEventClick: function(e, el, more)
16175 this.calpopover.placement = 'right';
16176 this.calpopover.setTitle('More');
16178 this.calpopover.setContent('');
16180 var ctr = this.calpopover.el.select('.popover-content', true).first();
16182 Roo.each(more, function(m){
16184 cls : 'fc-event-hori fc-event-draggable',
16187 var cg = ctr.createChild(cfg);
16189 cg.on('click', _this.onEventClick, _this, m);
16192 this.calpopover.show(el);
16197 onLoad: function ()
16199 this.calevents = [];
16202 if(this.store.getCount() > 0){
16203 this.store.data.each(function(d){
16206 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16207 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16208 time : d.data.start_time,
16209 title : d.data.title,
16210 description : d.data.description,
16211 venue : d.data.venue
16216 this.renderEvents();
16218 if(this.calevents.length && this.loadMask){
16219 this.maskEl.hide();
16223 onBeforeLoad: function()
16225 this.clearEvents();
16227 this.maskEl.show();
16241 * @class Roo.bootstrap.Popover
16242 * @extends Roo.bootstrap.Component
16243 * Bootstrap Popover class
16244 * @cfg {String} html contents of the popover (or false to use children..)
16245 * @cfg {String} title of popover (or false to hide)
16246 * @cfg {String} placement how it is placed
16247 * @cfg {String} trigger click || hover (or false to trigger manually)
16248 * @cfg {String} over what (parent or false to trigger manually.)
16249 * @cfg {Number} delay - delay before showing
16252 * Create a new Popover
16253 * @param {Object} config The config object
16256 Roo.bootstrap.Popover = function(config){
16257 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16263 * After the popover show
16265 * @param {Roo.bootstrap.Popover} this
16270 * After the popover hide
16272 * @param {Roo.bootstrap.Popover} this
16278 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16280 title: 'Fill in a title',
16283 placement : 'right',
16284 trigger : 'hover', // hover
16290 can_build_overlaid : false,
16292 getChildContainer : function()
16294 return this.el.select('.popover-content',true).first();
16297 getAutoCreate : function(){
16300 cls : 'popover roo-dynamic',
16301 style: 'display:block',
16307 cls : 'popover-inner',
16311 cls: 'popover-title',
16315 cls : 'popover-content',
16326 setTitle: function(str)
16329 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16331 setContent: function(str)
16334 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16336 // as it get's added to the bottom of the page.
16337 onRender : function(ct, position)
16339 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16341 var cfg = Roo.apply({}, this.getAutoCreate());
16345 cfg.cls += ' ' + this.cls;
16348 cfg.style = this.style;
16350 //Roo.log("adding to ");
16351 this.el = Roo.get(document.body).createChild(cfg, position);
16352 // Roo.log(this.el);
16357 initEvents : function()
16359 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16360 this.el.enableDisplayMode('block');
16362 if (this.over === false) {
16365 if (this.triggers === false) {
16368 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16369 var triggers = this.trigger ? this.trigger.split(' ') : [];
16370 Roo.each(triggers, function(trigger) {
16372 if (trigger == 'click') {
16373 on_el.on('click', this.toggle, this);
16374 } else if (trigger != 'manual') {
16375 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16376 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16378 on_el.on(eventIn ,this.enter, this);
16379 on_el.on(eventOut, this.leave, this);
16390 toggle : function () {
16391 this.hoverState == 'in' ? this.leave() : this.enter();
16394 enter : function () {
16396 clearTimeout(this.timeout);
16398 this.hoverState = 'in';
16400 if (!this.delay || !this.delay.show) {
16405 this.timeout = setTimeout(function () {
16406 if (_t.hoverState == 'in') {
16409 }, this.delay.show)
16412 leave : function() {
16413 clearTimeout(this.timeout);
16415 this.hoverState = 'out';
16417 if (!this.delay || !this.delay.hide) {
16422 this.timeout = setTimeout(function () {
16423 if (_t.hoverState == 'out') {
16426 }, this.delay.hide)
16429 show : function (on_el)
16432 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16436 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16437 if (this.html !== false) {
16438 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16440 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16441 if (!this.title.length) {
16442 this.el.select('.popover-title',true).hide();
16445 var placement = typeof this.placement == 'function' ?
16446 this.placement.call(this, this.el, on_el) :
16449 var autoToken = /\s?auto?\s?/i;
16450 var autoPlace = autoToken.test(placement);
16452 placement = placement.replace(autoToken, '') || 'top';
16456 //this.el.setXY([0,0]);
16458 this.el.dom.style.display='block';
16459 this.el.addClass(placement);
16461 //this.el.appendTo(on_el);
16463 var p = this.getPosition();
16464 var box = this.el.getBox();
16469 var align = Roo.bootstrap.Popover.alignment[placement];
16470 this.el.alignTo(on_el, align[0],align[1]);
16471 //var arrow = this.el.select('.arrow',true).first();
16472 //arrow.set(align[2],
16474 this.el.addClass('in');
16477 if (this.el.hasClass('fade')) {
16481 this.hoverState = 'in';
16483 this.fireEvent('show', this);
16488 this.el.setXY([0,0]);
16489 this.el.removeClass('in');
16491 this.hoverState = null;
16493 this.fireEvent('hide', this);
16498 Roo.bootstrap.Popover.alignment = {
16499 'left' : ['r-l', [-10,0], 'right'],
16500 'right' : ['l-r', [10,0], 'left'],
16501 'bottom' : ['t-b', [0,10], 'top'],
16502 'top' : [ 'b-t', [0,-10], 'bottom']
16513 * @class Roo.bootstrap.Progress
16514 * @extends Roo.bootstrap.Component
16515 * Bootstrap Progress class
16516 * @cfg {Boolean} striped striped of the progress bar
16517 * @cfg {Boolean} active animated of the progress bar
16521 * Create a new Progress
16522 * @param {Object} config The config object
16525 Roo.bootstrap.Progress = function(config){
16526 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16529 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16534 getAutoCreate : function(){
16542 cfg.cls += ' progress-striped';
16546 cfg.cls += ' active';
16565 * @class Roo.bootstrap.ProgressBar
16566 * @extends Roo.bootstrap.Component
16567 * Bootstrap ProgressBar class
16568 * @cfg {Number} aria_valuenow aria-value now
16569 * @cfg {Number} aria_valuemin aria-value min
16570 * @cfg {Number} aria_valuemax aria-value max
16571 * @cfg {String} label label for the progress bar
16572 * @cfg {String} panel (success | info | warning | danger )
16573 * @cfg {String} role role of the progress bar
16574 * @cfg {String} sr_only text
16578 * Create a new ProgressBar
16579 * @param {Object} config The config object
16582 Roo.bootstrap.ProgressBar = function(config){
16583 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16586 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16590 aria_valuemax : 100,
16596 getAutoCreate : function()
16601 cls: 'progress-bar',
16602 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16614 cfg.role = this.role;
16617 if(this.aria_valuenow){
16618 cfg['aria-valuenow'] = this.aria_valuenow;
16621 if(this.aria_valuemin){
16622 cfg['aria-valuemin'] = this.aria_valuemin;
16625 if(this.aria_valuemax){
16626 cfg['aria-valuemax'] = this.aria_valuemax;
16629 if(this.label && !this.sr_only){
16630 cfg.html = this.label;
16634 cfg.cls += ' progress-bar-' + this.panel;
16640 update : function(aria_valuenow)
16642 this.aria_valuenow = aria_valuenow;
16644 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16659 * @class Roo.bootstrap.TabGroup
16660 * @extends Roo.bootstrap.Column
16661 * Bootstrap Column class
16662 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16663 * @cfg {Boolean} carousel true to make the group behave like a carousel
16664 * @cfg {Boolean} bullets show bullets for the panels
16665 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16666 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16667 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16668 * @cfg {Boolean} showarrow (true|false) show arrow default true
16671 * Create a new TabGroup
16672 * @param {Object} config The config object
16675 Roo.bootstrap.TabGroup = function(config){
16676 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16678 this.navId = Roo.id();
16681 Roo.bootstrap.TabGroup.register(this);
16685 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16688 transition : false,
16693 slideOnTouch : false,
16696 getAutoCreate : function()
16698 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16700 cfg.cls += ' tab-content';
16702 if (this.carousel) {
16703 cfg.cls += ' carousel slide';
16706 cls : 'carousel-inner',
16710 if(this.bullets && !Roo.isTouch){
16713 cls : 'carousel-bullets',
16717 if(this.bullets_cls){
16718 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16725 cfg.cn[0].cn.push(bullets);
16728 if(this.showarrow){
16729 cfg.cn[0].cn.push({
16731 class : 'carousel-arrow',
16735 class : 'carousel-prev',
16739 class : 'fa fa-chevron-left'
16745 class : 'carousel-next',
16749 class : 'fa fa-chevron-right'
16762 initEvents: function()
16764 if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16765 this.el.on("touchstart", this.onTouchStart, this);
16768 if(this.autoslide){
16771 this.slideFn = window.setInterval(function() {
16772 _this.showPanelNext();
16776 if(this.showarrow){
16777 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16778 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16784 onTouchStart : function(e, el, o)
16786 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16790 this.showPanelNext();
16793 getChildContainer : function()
16795 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16799 * register a Navigation item
16800 * @param {Roo.bootstrap.NavItem} the navitem to add
16802 register : function(item)
16804 this.tabs.push( item);
16805 item.navId = this.navId; // not really needed..
16810 getActivePanel : function()
16813 Roo.each(this.tabs, function(t) {
16823 getPanelByName : function(n)
16826 Roo.each(this.tabs, function(t) {
16827 if (t.tabId == n) {
16835 indexOfPanel : function(p)
16838 Roo.each(this.tabs, function(t,i) {
16839 if (t.tabId == p.tabId) {
16848 * show a specific panel
16849 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16850 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16852 showPanel : function (pan)
16854 if(this.transition || typeof(pan) == 'undefined'){
16855 Roo.log("waiting for the transitionend");
16859 if (typeof(pan) == 'number') {
16860 pan = this.tabs[pan];
16863 if (typeof(pan) == 'string') {
16864 pan = this.getPanelByName(pan);
16867 var cur = this.getActivePanel();
16870 Roo.log('pan or acitve pan is undefined');
16874 if (pan.tabId == this.getActivePanel().tabId) {
16878 if (false === cur.fireEvent('beforedeactivate')) {
16882 if(this.bullets > 0 && !Roo.isTouch){
16883 this.setActiveBullet(this.indexOfPanel(pan));
16886 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16888 this.transition = true;
16889 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16890 var lr = dir == 'next' ? 'left' : 'right';
16891 pan.el.addClass(dir); // or prev
16892 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16893 cur.el.addClass(lr); // or right
16894 pan.el.addClass(lr);
16897 cur.el.on('transitionend', function() {
16898 Roo.log("trans end?");
16900 pan.el.removeClass([lr,dir]);
16901 pan.setActive(true);
16903 cur.el.removeClass([lr]);
16904 cur.setActive(false);
16906 _this.transition = false;
16908 }, this, { single: true } );
16913 cur.setActive(false);
16914 pan.setActive(true);
16919 showPanelNext : function()
16921 var i = this.indexOfPanel(this.getActivePanel());
16923 if (i >= this.tabs.length - 1 && !this.autoslide) {
16927 if (i >= this.tabs.length - 1 && this.autoslide) {
16931 this.showPanel(this.tabs[i+1]);
16934 showPanelPrev : function()
16936 var i = this.indexOfPanel(this.getActivePanel());
16938 if (i < 1 && !this.autoslide) {
16942 if (i < 1 && this.autoslide) {
16943 i = this.tabs.length;
16946 this.showPanel(this.tabs[i-1]);
16950 addBullet: function()
16952 if(!this.bullets || Roo.isTouch){
16955 var ctr = this.el.select('.carousel-bullets',true).first();
16956 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16957 var bullet = ctr.createChild({
16958 cls : 'bullet bullet-' + i
16959 },ctr.dom.lastChild);
16964 bullet.on('click', (function(e, el, o, ii, t){
16966 e.preventDefault();
16968 this.showPanel(ii);
16970 if(this.autoslide && this.slideFn){
16971 clearInterval(this.slideFn);
16972 this.slideFn = window.setInterval(function() {
16973 _this.showPanelNext();
16977 }).createDelegate(this, [i, bullet], true));
16982 setActiveBullet : function(i)
16988 Roo.each(this.el.select('.bullet', true).elements, function(el){
16989 el.removeClass('selected');
16992 var bullet = this.el.select('.bullet-' + i, true).first();
16998 bullet.addClass('selected');
17009 Roo.apply(Roo.bootstrap.TabGroup, {
17013 * register a Navigation Group
17014 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17016 register : function(navgrp)
17018 this.groups[navgrp.navId] = navgrp;
17022 * fetch a Navigation Group based on the navigation ID
17023 * if one does not exist , it will get created.
17024 * @param {string} the navgroup to add
17025 * @returns {Roo.bootstrap.NavGroup} the navgroup
17027 get: function(navId) {
17028 if (typeof(this.groups[navId]) == 'undefined') {
17029 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17031 return this.groups[navId] ;
17046 * @class Roo.bootstrap.TabPanel
17047 * @extends Roo.bootstrap.Component
17048 * Bootstrap TabPanel class
17049 * @cfg {Boolean} active panel active
17050 * @cfg {String} html panel content
17051 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17052 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17053 * @cfg {String} href click to link..
17057 * Create a new TabPanel
17058 * @param {Object} config The config object
17061 Roo.bootstrap.TabPanel = function(config){
17062 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17066 * Fires when the active status changes
17067 * @param {Roo.bootstrap.TabPanel} this
17068 * @param {Boolean} state the new state
17073 * @event beforedeactivate
17074 * Fires before a tab is de-activated - can be used to do validation on a form.
17075 * @param {Roo.bootstrap.TabPanel} this
17076 * @return {Boolean} false if there is an error
17079 'beforedeactivate': true
17082 this.tabId = this.tabId || Roo.id();
17086 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17094 getAutoCreate : function(){
17097 // item is needed for carousel - not sure if it has any effect otherwise
17098 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17099 html: this.html || ''
17103 cfg.cls += ' active';
17107 cfg.tabId = this.tabId;
17114 initEvents: function()
17116 var p = this.parent();
17117 this.navId = this.navId || p.navId;
17119 if (typeof(this.navId) != 'undefined') {
17120 // not really needed.. but just in case.. parent should be a NavGroup.
17121 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17125 var i = tg.tabs.length - 1;
17127 if(this.active && tg.bullets > 0 && i < tg.bullets){
17128 tg.setActiveBullet(i);
17132 if(this.href.length){
17133 this.el.on('click', this.onClick, this);
17138 onRender : function(ct, position)
17140 // Roo.log("Call onRender: " + this.xtype);
17142 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17150 setActive: function(state)
17152 Roo.log("panel - set active " + this.tabId + "=" + state);
17154 this.active = state;
17156 this.el.removeClass('active');
17158 } else if (!this.el.hasClass('active')) {
17159 this.el.addClass('active');
17162 this.fireEvent('changed', this, state);
17165 onClick: function(e)
17167 e.preventDefault();
17169 window.location.href = this.href;
17186 * @class Roo.bootstrap.DateField
17187 * @extends Roo.bootstrap.Input
17188 * Bootstrap DateField class
17189 * @cfg {Number} weekStart default 0
17190 * @cfg {String} viewMode default empty, (months|years)
17191 * @cfg {String} minViewMode default empty, (months|years)
17192 * @cfg {Number} startDate default -Infinity
17193 * @cfg {Number} endDate default Infinity
17194 * @cfg {Boolean} todayHighlight default false
17195 * @cfg {Boolean} todayBtn default false
17196 * @cfg {Boolean} calendarWeeks default false
17197 * @cfg {Object} daysOfWeekDisabled default empty
17198 * @cfg {Boolean} singleMode default false (true | false)
17200 * @cfg {Boolean} keyboardNavigation default true
17201 * @cfg {String} language default en
17204 * Create a new DateField
17205 * @param {Object} config The config object
17208 Roo.bootstrap.DateField = function(config){
17209 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17213 * Fires when this field show.
17214 * @param {Roo.bootstrap.DateField} this
17215 * @param {Mixed} date The date value
17220 * Fires when this field hide.
17221 * @param {Roo.bootstrap.DateField} this
17222 * @param {Mixed} date The date value
17227 * Fires when select a date.
17228 * @param {Roo.bootstrap.DateField} this
17229 * @param {Mixed} date The date value
17233 * @event beforeselect
17234 * Fires when before select a date.
17235 * @param {Roo.bootstrap.DateField} this
17236 * @param {Mixed} date The date value
17238 beforeselect : true
17242 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17245 * @cfg {String} format
17246 * The default date format string which can be overriden for localization support. The format must be
17247 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17251 * @cfg {String} altFormats
17252 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17253 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17255 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17263 todayHighlight : false,
17269 keyboardNavigation: true,
17271 calendarWeeks: false,
17273 startDate: -Infinity,
17277 daysOfWeekDisabled: [],
17281 singleMode : false,
17283 UTCDate: function()
17285 return new Date(Date.UTC.apply(Date, arguments));
17288 UTCToday: function()
17290 var today = new Date();
17291 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17294 getDate: function() {
17295 var d = this.getUTCDate();
17296 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17299 getUTCDate: function() {
17303 setDate: function(d) {
17304 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17307 setUTCDate: function(d) {
17309 this.setValue(this.formatDate(this.date));
17312 onRender: function(ct, position)
17315 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17317 this.language = this.language || 'en';
17318 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17319 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17321 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17322 this.format = this.format || 'm/d/y';
17323 this.isInline = false;
17324 this.isInput = true;
17325 this.component = this.el.select('.add-on', true).first() || false;
17326 this.component = (this.component && this.component.length === 0) ? false : this.component;
17327 this.hasInput = this.component && this.inputEL().length;
17329 if (typeof(this.minViewMode === 'string')) {
17330 switch (this.minViewMode) {
17332 this.minViewMode = 1;
17335 this.minViewMode = 2;
17338 this.minViewMode = 0;
17343 if (typeof(this.viewMode === 'string')) {
17344 switch (this.viewMode) {
17357 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17359 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17361 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17363 this.picker().on('mousedown', this.onMousedown, this);
17364 this.picker().on('click', this.onClick, this);
17366 this.picker().addClass('datepicker-dropdown');
17368 this.startViewMode = this.viewMode;
17370 if(this.singleMode){
17371 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17372 v.setVisibilityMode(Roo.Element.DISPLAY);
17376 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17377 v.setStyle('width', '189px');
17381 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17382 if(!this.calendarWeeks){
17387 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17388 v.attr('colspan', function(i, val){
17389 return parseInt(val) + 1;
17394 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17396 this.setStartDate(this.startDate);
17397 this.setEndDate(this.endDate);
17399 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17406 if(this.isInline) {
17411 picker : function()
17413 return this.pickerEl;
17414 // return this.el.select('.datepicker', true).first();
17417 fillDow: function()
17419 var dowCnt = this.weekStart;
17428 if(this.calendarWeeks){
17436 while (dowCnt < this.weekStart + 7) {
17440 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17444 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17447 fillMonths: function()
17450 var months = this.picker().select('>.datepicker-months td', true).first();
17452 months.dom.innerHTML = '';
17458 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17461 months.createChild(month);
17468 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;
17470 if (this.date < this.startDate) {
17471 this.viewDate = new Date(this.startDate);
17472 } else if (this.date > this.endDate) {
17473 this.viewDate = new Date(this.endDate);
17475 this.viewDate = new Date(this.date);
17483 var d = new Date(this.viewDate),
17484 year = d.getUTCFullYear(),
17485 month = d.getUTCMonth(),
17486 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17487 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17488 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17489 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17490 currentDate = this.date && this.date.valueOf(),
17491 today = this.UTCToday();
17493 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17495 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17497 // this.picker.select('>tfoot th.today').
17498 // .text(dates[this.language].today)
17499 // .toggle(this.todayBtn !== false);
17501 this.updateNavArrows();
17504 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17506 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17508 prevMonth.setUTCDate(day);
17510 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17512 var nextMonth = new Date(prevMonth);
17514 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17516 nextMonth = nextMonth.valueOf();
17518 var fillMonths = false;
17520 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17522 while(prevMonth.valueOf() < nextMonth) {
17525 if (prevMonth.getUTCDay() === this.weekStart) {
17527 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17535 if(this.calendarWeeks){
17536 // ISO 8601: First week contains first thursday.
17537 // ISO also states week starts on Monday, but we can be more abstract here.
17539 // Start of current week: based on weekstart/current date
17540 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17541 // Thursday of this week
17542 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17543 // First Thursday of year, year from thursday
17544 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17545 // Calendar week: ms between thursdays, div ms per day, div 7 days
17546 calWeek = (th - yth) / 864e5 / 7 + 1;
17548 fillMonths.cn.push({
17556 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17558 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17561 if (this.todayHighlight &&
17562 prevMonth.getUTCFullYear() == today.getFullYear() &&
17563 prevMonth.getUTCMonth() == today.getMonth() &&
17564 prevMonth.getUTCDate() == today.getDate()) {
17565 clsName += ' today';
17568 if (currentDate && prevMonth.valueOf() === currentDate) {
17569 clsName += ' active';
17572 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17573 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17574 clsName += ' disabled';
17577 fillMonths.cn.push({
17579 cls: 'day ' + clsName,
17580 html: prevMonth.getDate()
17583 prevMonth.setDate(prevMonth.getDate()+1);
17586 var currentYear = this.date && this.date.getUTCFullYear();
17587 var currentMonth = this.date && this.date.getUTCMonth();
17589 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17591 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17592 v.removeClass('active');
17594 if(currentYear === year && k === currentMonth){
17595 v.addClass('active');
17598 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17599 v.addClass('disabled');
17605 year = parseInt(year/10, 10) * 10;
17607 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17609 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17612 for (var i = -1; i < 11; i++) {
17613 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17615 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17623 showMode: function(dir)
17626 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17629 Roo.each(this.picker().select('>div',true).elements, function(v){
17630 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17633 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17638 if(this.isInline) {
17642 this.picker().removeClass(['bottom', 'top']);
17644 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17646 * place to the top of element!
17650 this.picker().addClass('top');
17651 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17656 this.picker().addClass('bottom');
17658 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17661 parseDate : function(value)
17663 if(!value || value instanceof Date){
17666 var v = Date.parseDate(value, this.format);
17667 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17668 v = Date.parseDate(value, 'Y-m-d');
17670 if(!v && this.altFormats){
17671 if(!this.altFormatsArray){
17672 this.altFormatsArray = this.altFormats.split("|");
17674 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17675 v = Date.parseDate(value, this.altFormatsArray[i]);
17681 formatDate : function(date, fmt)
17683 return (!date || !(date instanceof Date)) ?
17684 date : date.dateFormat(fmt || this.format);
17687 onFocus : function()
17689 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17693 onBlur : function()
17695 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17697 var d = this.inputEl().getValue();
17706 this.picker().show();
17710 this.fireEvent('show', this, this.date);
17715 if(this.isInline) {
17718 this.picker().hide();
17719 this.viewMode = this.startViewMode;
17722 this.fireEvent('hide', this, this.date);
17726 onMousedown: function(e)
17728 e.stopPropagation();
17729 e.preventDefault();
17734 Roo.bootstrap.DateField.superclass.keyup.call(this);
17738 setValue: function(v)
17740 if(this.fireEvent('beforeselect', this, v) !== false){
17741 var d = new Date(this.parseDate(v) ).clearTime();
17743 if(isNaN(d.getTime())){
17744 this.date = this.viewDate = '';
17745 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17749 v = this.formatDate(d);
17751 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17753 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17757 this.fireEvent('select', this, this.date);
17761 getValue: function()
17763 return this.formatDate(this.date);
17766 fireKey: function(e)
17768 if (!this.picker().isVisible()){
17769 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17775 var dateChanged = false,
17777 newDate, newViewDate;
17782 e.preventDefault();
17786 if (!this.keyboardNavigation) {
17789 dir = e.keyCode == 37 ? -1 : 1;
17792 newDate = this.moveYear(this.date, dir);
17793 newViewDate = this.moveYear(this.viewDate, dir);
17794 } else if (e.shiftKey){
17795 newDate = this.moveMonth(this.date, dir);
17796 newViewDate = this.moveMonth(this.viewDate, dir);
17798 newDate = new Date(this.date);
17799 newDate.setUTCDate(this.date.getUTCDate() + dir);
17800 newViewDate = new Date(this.viewDate);
17801 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17803 if (this.dateWithinRange(newDate)){
17804 this.date = newDate;
17805 this.viewDate = newViewDate;
17806 this.setValue(this.formatDate(this.date));
17808 e.preventDefault();
17809 dateChanged = true;
17814 if (!this.keyboardNavigation) {
17817 dir = e.keyCode == 38 ? -1 : 1;
17819 newDate = this.moveYear(this.date, dir);
17820 newViewDate = this.moveYear(this.viewDate, dir);
17821 } else if (e.shiftKey){
17822 newDate = this.moveMonth(this.date, dir);
17823 newViewDate = this.moveMonth(this.viewDate, dir);
17825 newDate = new Date(this.date);
17826 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17827 newViewDate = new Date(this.viewDate);
17828 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17830 if (this.dateWithinRange(newDate)){
17831 this.date = newDate;
17832 this.viewDate = newViewDate;
17833 this.setValue(this.formatDate(this.date));
17835 e.preventDefault();
17836 dateChanged = true;
17840 this.setValue(this.formatDate(this.date));
17842 e.preventDefault();
17845 this.setValue(this.formatDate(this.date));
17859 onClick: function(e)
17861 e.stopPropagation();
17862 e.preventDefault();
17864 var target = e.getTarget();
17866 if(target.nodeName.toLowerCase() === 'i'){
17867 target = Roo.get(target).dom.parentNode;
17870 var nodeName = target.nodeName;
17871 var className = target.className;
17872 var html = target.innerHTML;
17873 //Roo.log(nodeName);
17875 switch(nodeName.toLowerCase()) {
17877 switch(className) {
17883 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17884 switch(this.viewMode){
17886 this.viewDate = this.moveMonth(this.viewDate, dir);
17890 this.viewDate = this.moveYear(this.viewDate, dir);
17896 var date = new Date();
17897 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17899 this.setValue(this.formatDate(this.date));
17906 if (className.indexOf('disabled') < 0) {
17907 this.viewDate.setUTCDate(1);
17908 if (className.indexOf('month') > -1) {
17909 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17911 var year = parseInt(html, 10) || 0;
17912 this.viewDate.setUTCFullYear(year);
17916 if(this.singleMode){
17917 this.setValue(this.formatDate(this.viewDate));
17928 //Roo.log(className);
17929 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17930 var day = parseInt(html, 10) || 1;
17931 var year = this.viewDate.getUTCFullYear(),
17932 month = this.viewDate.getUTCMonth();
17934 if (className.indexOf('old') > -1) {
17941 } else if (className.indexOf('new') > -1) {
17949 //Roo.log([year,month,day]);
17950 this.date = this.UTCDate(year, month, day,0,0,0,0);
17951 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17953 //Roo.log(this.formatDate(this.date));
17954 this.setValue(this.formatDate(this.date));
17961 setStartDate: function(startDate)
17963 this.startDate = startDate || -Infinity;
17964 if (this.startDate !== -Infinity) {
17965 this.startDate = this.parseDate(this.startDate);
17968 this.updateNavArrows();
17971 setEndDate: function(endDate)
17973 this.endDate = endDate || Infinity;
17974 if (this.endDate !== Infinity) {
17975 this.endDate = this.parseDate(this.endDate);
17978 this.updateNavArrows();
17981 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17983 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17984 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17985 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17987 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17988 return parseInt(d, 10);
17991 this.updateNavArrows();
17994 updateNavArrows: function()
17996 if(this.singleMode){
18000 var d = new Date(this.viewDate),
18001 year = d.getUTCFullYear(),
18002 month = d.getUTCMonth();
18004 Roo.each(this.picker().select('.prev', true).elements, function(v){
18006 switch (this.viewMode) {
18009 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18015 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18022 Roo.each(this.picker().select('.next', true).elements, function(v){
18024 switch (this.viewMode) {
18027 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18033 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18041 moveMonth: function(date, dir)
18046 var new_date = new Date(date.valueOf()),
18047 day = new_date.getUTCDate(),
18048 month = new_date.getUTCMonth(),
18049 mag = Math.abs(dir),
18051 dir = dir > 0 ? 1 : -1;
18054 // If going back one month, make sure month is not current month
18055 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18057 return new_date.getUTCMonth() == month;
18059 // If going forward one month, make sure month is as expected
18060 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18062 return new_date.getUTCMonth() != new_month;
18064 new_month = month + dir;
18065 new_date.setUTCMonth(new_month);
18066 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18067 if (new_month < 0 || new_month > 11) {
18068 new_month = (new_month + 12) % 12;
18071 // For magnitudes >1, move one month at a time...
18072 for (var i=0; i<mag; i++) {
18073 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18074 new_date = this.moveMonth(new_date, dir);
18076 // ...then reset the day, keeping it in the new month
18077 new_month = new_date.getUTCMonth();
18078 new_date.setUTCDate(day);
18080 return new_month != new_date.getUTCMonth();
18083 // Common date-resetting loop -- if date is beyond end of month, make it
18086 new_date.setUTCDate(--day);
18087 new_date.setUTCMonth(new_month);
18092 moveYear: function(date, dir)
18094 return this.moveMonth(date, dir*12);
18097 dateWithinRange: function(date)
18099 return date >= this.startDate && date <= this.endDate;
18105 this.picker().remove();
18110 Roo.apply(Roo.bootstrap.DateField, {
18121 html: '<i class="fa fa-arrow-left"/>'
18131 html: '<i class="fa fa-arrow-right"/>'
18173 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18174 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18175 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18176 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18177 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18190 navFnc: 'FullYear',
18195 navFnc: 'FullYear',
18200 Roo.apply(Roo.bootstrap.DateField, {
18204 cls: 'datepicker dropdown-menu roo-dynamic',
18208 cls: 'datepicker-days',
18212 cls: 'table-condensed',
18214 Roo.bootstrap.DateField.head,
18218 Roo.bootstrap.DateField.footer
18225 cls: 'datepicker-months',
18229 cls: 'table-condensed',
18231 Roo.bootstrap.DateField.head,
18232 Roo.bootstrap.DateField.content,
18233 Roo.bootstrap.DateField.footer
18240 cls: 'datepicker-years',
18244 cls: 'table-condensed',
18246 Roo.bootstrap.DateField.head,
18247 Roo.bootstrap.DateField.content,
18248 Roo.bootstrap.DateField.footer
18267 * @class Roo.bootstrap.TimeField
18268 * @extends Roo.bootstrap.Input
18269 * Bootstrap DateField class
18273 * Create a new TimeField
18274 * @param {Object} config The config object
18277 Roo.bootstrap.TimeField = function(config){
18278 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18282 * Fires when this field show.
18283 * @param {Roo.bootstrap.DateField} thisthis
18284 * @param {Mixed} date The date value
18289 * Fires when this field hide.
18290 * @param {Roo.bootstrap.DateField} this
18291 * @param {Mixed} date The date value
18296 * Fires when select a date.
18297 * @param {Roo.bootstrap.DateField} this
18298 * @param {Mixed} date The date value
18304 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18307 * @cfg {String} format
18308 * The default time format string which can be overriden for localization support. The format must be
18309 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18313 onRender: function(ct, position)
18316 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18318 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18320 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18322 this.pop = this.picker().select('>.datepicker-time',true).first();
18323 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18325 this.picker().on('mousedown', this.onMousedown, this);
18326 this.picker().on('click', this.onClick, this);
18328 this.picker().addClass('datepicker-dropdown');
18333 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18334 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18335 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18336 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18337 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18338 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18342 fireKey: function(e){
18343 if (!this.picker().isVisible()){
18344 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18350 e.preventDefault();
18358 this.onTogglePeriod();
18361 this.onIncrementMinutes();
18364 this.onDecrementMinutes();
18373 onClick: function(e) {
18374 e.stopPropagation();
18375 e.preventDefault();
18378 picker : function()
18380 return this.el.select('.datepicker', true).first();
18383 fillTime: function()
18385 var time = this.pop.select('tbody', true).first();
18387 time.dom.innerHTML = '';
18402 cls: 'hours-up glyphicon glyphicon-chevron-up'
18422 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18443 cls: 'timepicker-hour',
18458 cls: 'timepicker-minute',
18473 cls: 'btn btn-primary period',
18495 cls: 'hours-down glyphicon glyphicon-chevron-down'
18515 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18533 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18540 var hours = this.time.getHours();
18541 var minutes = this.time.getMinutes();
18554 hours = hours - 12;
18558 hours = '0' + hours;
18562 minutes = '0' + minutes;
18565 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18566 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18567 this.pop.select('button', true).first().dom.innerHTML = period;
18573 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18575 var cls = ['bottom'];
18577 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18584 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18589 this.picker().addClass(cls.join('-'));
18593 Roo.each(cls, function(c){
18595 _this.picker().setTop(_this.inputEl().getHeight());
18599 _this.picker().setTop(0 - _this.picker().getHeight());
18604 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18608 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18615 onFocus : function()
18617 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18621 onBlur : function()
18623 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18629 this.picker().show();
18634 this.fireEvent('show', this, this.date);
18639 this.picker().hide();
18642 this.fireEvent('hide', this, this.date);
18645 setTime : function()
18648 this.setValue(this.time.format(this.format));
18650 this.fireEvent('select', this, this.date);
18655 onMousedown: function(e){
18656 e.stopPropagation();
18657 e.preventDefault();
18660 onIncrementHours: function()
18662 Roo.log('onIncrementHours');
18663 this.time = this.time.add(Date.HOUR, 1);
18668 onDecrementHours: function()
18670 Roo.log('onDecrementHours');
18671 this.time = this.time.add(Date.HOUR, -1);
18675 onIncrementMinutes: function()
18677 Roo.log('onIncrementMinutes');
18678 this.time = this.time.add(Date.MINUTE, 1);
18682 onDecrementMinutes: function()
18684 Roo.log('onDecrementMinutes');
18685 this.time = this.time.add(Date.MINUTE, -1);
18689 onTogglePeriod: function()
18691 Roo.log('onTogglePeriod');
18692 this.time = this.time.add(Date.HOUR, 12);
18699 Roo.apply(Roo.bootstrap.TimeField, {
18729 cls: 'btn btn-info ok',
18741 Roo.apply(Roo.bootstrap.TimeField, {
18745 cls: 'datepicker dropdown-menu',
18749 cls: 'datepicker-time',
18753 cls: 'table-condensed',
18755 Roo.bootstrap.TimeField.content,
18756 Roo.bootstrap.TimeField.footer
18775 * @class Roo.bootstrap.MonthField
18776 * @extends Roo.bootstrap.Input
18777 * Bootstrap MonthField class
18779 * @cfg {String} language default en
18782 * Create a new MonthField
18783 * @param {Object} config The config object
18786 Roo.bootstrap.MonthField = function(config){
18787 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18792 * Fires when this field show.
18793 * @param {Roo.bootstrap.MonthField} this
18794 * @param {Mixed} date The date value
18799 * Fires when this field hide.
18800 * @param {Roo.bootstrap.MonthField} this
18801 * @param {Mixed} date The date value
18806 * Fires when select a date.
18807 * @param {Roo.bootstrap.MonthField} this
18808 * @param {String} oldvalue The old value
18809 * @param {String} newvalue The new value
18815 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18817 onRender: function(ct, position)
18820 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18822 this.language = this.language || 'en';
18823 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18824 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18826 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18827 this.isInline = false;
18828 this.isInput = true;
18829 this.component = this.el.select('.add-on', true).first() || false;
18830 this.component = (this.component && this.component.length === 0) ? false : this.component;
18831 this.hasInput = this.component && this.inputEL().length;
18833 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18835 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18837 this.picker().on('mousedown', this.onMousedown, this);
18838 this.picker().on('click', this.onClick, this);
18840 this.picker().addClass('datepicker-dropdown');
18842 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18843 v.setStyle('width', '189px');
18850 if(this.isInline) {
18856 setValue: function(v, suppressEvent)
18858 var o = this.getValue();
18860 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18864 if(suppressEvent !== true){
18865 this.fireEvent('select', this, o, v);
18870 getValue: function()
18875 onClick: function(e)
18877 e.stopPropagation();
18878 e.preventDefault();
18880 var target = e.getTarget();
18882 if(target.nodeName.toLowerCase() === 'i'){
18883 target = Roo.get(target).dom.parentNode;
18886 var nodeName = target.nodeName;
18887 var className = target.className;
18888 var html = target.innerHTML;
18890 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18894 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18896 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18902 picker : function()
18904 return this.pickerEl;
18907 fillMonths: function()
18910 var months = this.picker().select('>.datepicker-months td', true).first();
18912 months.dom.innerHTML = '';
18918 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18921 months.createChild(month);
18930 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18931 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18934 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18935 e.removeClass('active');
18937 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18938 e.addClass('active');
18945 if(this.isInline) {
18949 this.picker().removeClass(['bottom', 'top']);
18951 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18953 * place to the top of element!
18957 this.picker().addClass('top');
18958 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18963 this.picker().addClass('bottom');
18965 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18968 onFocus : function()
18970 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18974 onBlur : function()
18976 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18978 var d = this.inputEl().getValue();
18987 this.picker().show();
18988 this.picker().select('>.datepicker-months', true).first().show();
18992 this.fireEvent('show', this, this.date);
18997 if(this.isInline) {
19000 this.picker().hide();
19001 this.fireEvent('hide', this, this.date);
19005 onMousedown: function(e)
19007 e.stopPropagation();
19008 e.preventDefault();
19013 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19017 fireKey: function(e)
19019 if (!this.picker().isVisible()){
19020 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19031 e.preventDefault();
19035 dir = e.keyCode == 37 ? -1 : 1;
19037 this.vIndex = this.vIndex + dir;
19039 if(this.vIndex < 0){
19043 if(this.vIndex > 11){
19047 if(isNaN(this.vIndex)){
19051 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19057 dir = e.keyCode == 38 ? -1 : 1;
19059 this.vIndex = this.vIndex + dir * 4;
19061 if(this.vIndex < 0){
19065 if(this.vIndex > 11){
19069 if(isNaN(this.vIndex)){
19073 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19078 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19079 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19083 e.preventDefault();
19086 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19087 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19103 this.picker().remove();
19108 Roo.apply(Roo.bootstrap.MonthField, {
19127 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19128 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19133 Roo.apply(Roo.bootstrap.MonthField, {
19137 cls: 'datepicker dropdown-menu roo-dynamic',
19141 cls: 'datepicker-months',
19145 cls: 'table-condensed',
19147 Roo.bootstrap.DateField.content
19167 * @class Roo.bootstrap.CheckBox
19168 * @extends Roo.bootstrap.Input
19169 * Bootstrap CheckBox class
19171 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19172 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19173 * @cfg {String} boxLabel The text that appears beside the checkbox
19174 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19175 * @cfg {Boolean} checked initnal the element
19176 * @cfg {Boolean} inline inline the element (default false)
19177 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19180 * Create a new CheckBox
19181 * @param {Object} config The config object
19184 Roo.bootstrap.CheckBox = function(config){
19185 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19190 * Fires when the element is checked or unchecked.
19191 * @param {Roo.bootstrap.CheckBox} this This input
19192 * @param {Boolean} checked The new checked value
19199 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19201 inputType: 'checkbox',
19209 getAutoCreate : function()
19211 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19217 cfg.cls = 'form-group ' + this.inputType; //input-group
19220 cfg.cls += ' ' + this.inputType + '-inline';
19226 type : this.inputType,
19227 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19228 cls : 'roo-' + this.inputType, //'form-box',
19229 placeholder : this.placeholder || ''
19233 if (this.weight) { // Validity check?
19234 cfg.cls += " " + this.inputType + "-" + this.weight;
19237 if (this.disabled) {
19238 input.disabled=true;
19242 input.checked = this.checked;
19246 input.name = this.name;
19250 input.cls += ' input-' + this.size;
19255 ['xs','sm','md','lg'].map(function(size){
19256 if (settings[size]) {
19257 cfg.cls += ' col-' + size + '-' + settings[size];
19261 var inputblock = input;
19263 if (this.before || this.after) {
19266 cls : 'input-group',
19271 inputblock.cn.push({
19273 cls : 'input-group-addon',
19278 inputblock.cn.push(input);
19281 inputblock.cn.push({
19283 cls : 'input-group-addon',
19290 if (align ==='left' && this.fieldLabel.length) {
19291 // Roo.log("left and has label");
19297 cls : 'control-label col-md-' + this.labelWidth,
19298 html : this.fieldLabel
19302 cls : "col-md-" + (12 - this.labelWidth),
19309 } else if ( this.fieldLabel.length) {
19310 // Roo.log(" label");
19314 tag: this.boxLabel ? 'span' : 'label',
19316 cls: 'control-label box-input-label',
19317 //cls : 'input-group-addon',
19318 html : this.fieldLabel
19328 // Roo.log(" no label && no align");
19329 cfg.cn = [ inputblock ] ;
19335 var boxLabelCfg = {
19337 //'for': id, // box label is handled by onclick - so no for...
19339 html: this.boxLabel
19343 boxLabelCfg.tooltip = this.tooltip;
19346 cfg.cn.push(boxLabelCfg);
19356 * return the real input element.
19358 inputEl: function ()
19360 return this.el.select('input.roo-' + this.inputType,true).first();
19363 labelEl: function()
19365 return this.el.select('label.control-label',true).first();
19367 /* depricated... */
19371 return this.labelEl();
19374 boxLabelEl: function()
19376 return this.el.select('label.box-label',true).first();
19379 initEvents : function()
19381 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19383 this.inputEl().on('click', this.onClick, this);
19385 if (this.boxLabel) {
19386 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19389 this.startValue = this.getValue();
19392 Roo.bootstrap.CheckBox.register(this);
19396 onClick : function()
19398 this.setChecked(!this.checked);
19401 setChecked : function(state,suppressEvent)
19403 this.startValue = this.getValue();
19405 if(this.inputType == 'radio'){
19407 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19408 e.dom.checked = false;
19411 this.inputEl().dom.checked = true;
19413 this.inputEl().dom.value = this.inputValue;
19415 if(suppressEvent !== true){
19416 this.fireEvent('check', this, true);
19424 this.checked = state;
19426 this.inputEl().dom.checked = state;
19428 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19430 if(suppressEvent !== true){
19431 this.fireEvent('check', this, state);
19437 getValue : function()
19439 if(this.inputType == 'radio'){
19440 return this.getGroupValue();
19443 return this.inputEl().getValue();
19447 getGroupValue : function()
19449 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19453 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19456 setValue : function(v,suppressEvent)
19458 if(this.inputType == 'radio'){
19459 this.setGroupValue(v, suppressEvent);
19463 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19468 setGroupValue : function(v, suppressEvent)
19470 this.startValue = this.getValue();
19472 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19473 e.dom.checked = false;
19475 if(e.dom.value == v){
19476 e.dom.checked = true;
19480 if(suppressEvent !== true){
19481 this.fireEvent('check', this, true);
19489 validate : function()
19493 (this.inputType == 'radio' && this.validateRadio()) ||
19494 (this.inputType == 'checkbox' && this.validateCheckbox())
19500 this.markInvalid();
19504 validateRadio : function()
19508 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19509 if(!e.dom.checked){
19521 validateCheckbox : function()
19524 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19527 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19535 for(var i in group){
19540 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19547 * Mark this field as valid
19549 markValid : function()
19551 if(this.allowBlank){
19557 this.fireEvent('valid', this);
19559 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19562 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19569 if(this.inputType == 'radio'){
19570 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19571 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19572 e.findParent('.form-group', false, true).addClass(_this.validClass);
19579 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19580 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19584 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19590 for(var i in group){
19591 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19592 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19597 * Mark this field as invalid
19598 * @param {String} msg The validation message
19600 markInvalid : function(msg)
19602 if(this.allowBlank){
19608 this.fireEvent('invalid', this, msg);
19610 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19613 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19617 label.markInvalid();
19620 if(this.inputType == 'radio'){
19621 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19622 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19623 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19630 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19631 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19635 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19641 for(var i in group){
19642 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19643 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19650 Roo.apply(Roo.bootstrap.CheckBox, {
19655 * register a CheckBox Group
19656 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19658 register : function(checkbox)
19660 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19661 this.groups[checkbox.groupId] = {};
19664 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19668 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19672 * fetch a CheckBox Group based on the group ID
19673 * @param {string} the group ID
19674 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19676 get: function(groupId) {
19677 if (typeof(this.groups[groupId]) == 'undefined') {
19681 return this.groups[groupId] ;
19693 *<div class="radio">
19695 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19696 Option one is this and that—be sure to include why it's great
19703 *<label class="radio-inline">fieldLabel</label>
19704 *<label class="radio-inline">
19705 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19713 * @class Roo.bootstrap.Radio
19714 * @extends Roo.bootstrap.CheckBox
19715 * Bootstrap Radio class
19718 * Create a new Radio
19719 * @param {Object} config The config object
19722 Roo.bootstrap.Radio = function(config){
19723 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19727 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19729 inputType: 'radio',
19733 getAutoCreate : function()
19735 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19736 align = align || 'left'; // default...
19743 tag : this.inline ? 'span' : 'div',
19748 var inline = this.inline ? ' radio-inline' : '';
19752 // does not need for, as we wrap the input with it..
19754 cls : 'control-label box-label' + inline,
19757 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19761 //cls : 'control-label' + inline,
19762 html : this.fieldLabel,
19763 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19772 type : this.inputType,
19773 //value : (!this.checked) ? this.valueOff : this.inputValue,
19774 value : this.inputValue,
19776 placeholder : this.placeholder || '' // ?? needed????
19779 if (this.weight) { // Validity check?
19780 input.cls += " radio-" + this.weight;
19782 if (this.disabled) {
19783 input.disabled=true;
19787 input.checked = this.checked;
19791 input.name = this.name;
19795 input.cls += ' input-' + this.size;
19798 //?? can span's inline have a width??
19801 ['xs','sm','md','lg'].map(function(size){
19802 if (settings[size]) {
19803 cfg.cls += ' col-' + size + '-' + settings[size];
19807 var inputblock = input;
19809 if (this.before || this.after) {
19812 cls : 'input-group',
19817 inputblock.cn.push({
19819 cls : 'input-group-addon',
19823 inputblock.cn.push(input);
19825 inputblock.cn.push({
19827 cls : 'input-group-addon',
19835 if (this.fieldLabel && this.fieldLabel.length) {
19836 cfg.cn.push(fieldLabel);
19839 // normal bootstrap puts the input inside the label.
19840 // however with our styled version - it has to go after the input.
19842 //lbl.cn.push(inputblock);
19846 cls: 'radio' + inline,
19853 cfg.cn.push( lblwrap);
19858 html: this.boxLabel
19867 initEvents : function()
19869 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19871 this.inputEl().on('click', this.onClick, this);
19872 if (this.boxLabel) {
19873 //Roo.log('find label');
19874 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19879 inputEl: function ()
19881 return this.el.select('input.roo-radio',true).first();
19883 onClick : function()
19886 this.setChecked(true);
19889 setChecked : function(state,suppressEvent)
19892 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19893 v.dom.checked = false;
19896 Roo.log(this.inputEl().dom);
19897 this.checked = state;
19898 this.inputEl().dom.checked = state;
19900 if(suppressEvent !== true){
19901 this.fireEvent('check', this, state);
19904 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19908 getGroupValue : function()
19911 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19912 if(v.dom.checked == true){
19913 value = v.dom.value;
19921 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19922 * @return {Mixed} value The field value
19924 getValue : function(){
19925 return this.getGroupValue();
19931 //<script type="text/javascript">
19934 * Based Ext JS Library 1.1.1
19935 * Copyright(c) 2006-2007, Ext JS, LLC.
19941 * @class Roo.HtmlEditorCore
19942 * @extends Roo.Component
19943 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19945 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19948 Roo.HtmlEditorCore = function(config){
19951 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19956 * @event initialize
19957 * Fires when the editor is fully initialized (including the iframe)
19958 * @param {Roo.HtmlEditorCore} this
19963 * Fires when the editor is first receives the focus. Any insertion must wait
19964 * until after this event.
19965 * @param {Roo.HtmlEditorCore} this
19969 * @event beforesync
19970 * Fires before the textarea is updated with content from the editor iframe. Return false
19971 * to cancel the sync.
19972 * @param {Roo.HtmlEditorCore} this
19973 * @param {String} html
19977 * @event beforepush
19978 * Fires before the iframe editor is updated with content from the textarea. Return false
19979 * to cancel the push.
19980 * @param {Roo.HtmlEditorCore} this
19981 * @param {String} html
19986 * Fires when the textarea is updated with content from the editor iframe.
19987 * @param {Roo.HtmlEditorCore} this
19988 * @param {String} html
19993 * Fires when the iframe editor is updated with content from the textarea.
19994 * @param {Roo.HtmlEditorCore} this
19995 * @param {String} html
20000 * @event editorevent
20001 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20002 * @param {Roo.HtmlEditorCore} this
20008 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20010 // defaults : white / black...
20011 this.applyBlacklists();
20018 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20022 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20028 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20033 * @cfg {Number} height (in pixels)
20037 * @cfg {Number} width (in pixels)
20042 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20045 stylesheets: false,
20050 // private properties
20051 validationEvent : false,
20053 initialized : false,
20055 sourceEditMode : false,
20056 onFocus : Roo.emptyFn,
20058 hideMode:'offsets',
20062 // blacklist + whitelisted elements..
20069 * Protected method that will not generally be called directly. It
20070 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20071 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20073 getDocMarkup : function(){
20077 // inherit styels from page...??
20078 if (this.stylesheets === false) {
20080 Roo.get(document.head).select('style').each(function(node) {
20081 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20084 Roo.get(document.head).select('link').each(function(node) {
20085 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20088 } else if (!this.stylesheets.length) {
20090 st = '<style type="text/css">' +
20091 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20097 st += '<style type="text/css">' +
20098 'IMG { cursor: pointer } ' +
20102 return '<html><head>' + st +
20103 //<style type="text/css">' +
20104 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20106 ' </head><body class="roo-htmleditor-body"></body></html>';
20110 onRender : function(ct, position)
20113 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20114 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20117 this.el.dom.style.border = '0 none';
20118 this.el.dom.setAttribute('tabIndex', -1);
20119 this.el.addClass('x-hidden hide');
20123 if(Roo.isIE){ // fix IE 1px bogus margin
20124 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20128 this.frameId = Roo.id();
20132 var iframe = this.owner.wrap.createChild({
20134 cls: 'form-control', // bootstrap..
20136 name: this.frameId,
20137 frameBorder : 'no',
20138 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20143 this.iframe = iframe.dom;
20145 this.assignDocWin();
20147 this.doc.designMode = 'on';
20150 this.doc.write(this.getDocMarkup());
20154 var task = { // must defer to wait for browser to be ready
20156 //console.log("run task?" + this.doc.readyState);
20157 this.assignDocWin();
20158 if(this.doc.body || this.doc.readyState == 'complete'){
20160 this.doc.designMode="on";
20164 Roo.TaskMgr.stop(task);
20165 this.initEditor.defer(10, this);
20172 Roo.TaskMgr.start(task);
20177 onResize : function(w, h)
20179 Roo.log('resize: ' +w + ',' + h );
20180 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20184 if(typeof w == 'number'){
20186 this.iframe.style.width = w + 'px';
20188 if(typeof h == 'number'){
20190 this.iframe.style.height = h + 'px';
20192 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20199 * Toggles the editor between standard and source edit mode.
20200 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20202 toggleSourceEdit : function(sourceEditMode){
20204 this.sourceEditMode = sourceEditMode === true;
20206 if(this.sourceEditMode){
20208 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20211 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20212 //this.iframe.className = '';
20215 //this.setSize(this.owner.wrap.getSize());
20216 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20223 * Protected method that will not generally be called directly. If you need/want
20224 * custom HTML cleanup, this is the method you should override.
20225 * @param {String} html The HTML to be cleaned
20226 * return {String} The cleaned HTML
20228 cleanHtml : function(html){
20229 html = String(html);
20230 if(html.length > 5){
20231 if(Roo.isSafari){ // strip safari nonsense
20232 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20235 if(html == ' '){
20242 * HTML Editor -> Textarea
20243 * Protected method that will not generally be called directly. Syncs the contents
20244 * of the editor iframe with the textarea.
20246 syncValue : function(){
20247 if(this.initialized){
20248 var bd = (this.doc.body || this.doc.documentElement);
20249 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20250 var html = bd.innerHTML;
20252 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20253 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20255 html = '<div style="'+m[0]+'">' + html + '</div>';
20258 html = this.cleanHtml(html);
20259 // fix up the special chars.. normaly like back quotes in word...
20260 // however we do not want to do this with chinese..
20261 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20262 var cc = b.charCodeAt();
20264 (cc >= 0x4E00 && cc < 0xA000 ) ||
20265 (cc >= 0x3400 && cc < 0x4E00 ) ||
20266 (cc >= 0xf900 && cc < 0xfb00 )
20272 if(this.owner.fireEvent('beforesync', this, html) !== false){
20273 this.el.dom.value = html;
20274 this.owner.fireEvent('sync', this, html);
20280 * Protected method that will not generally be called directly. Pushes the value of the textarea
20281 * into the iframe editor.
20283 pushValue : function(){
20284 if(this.initialized){
20285 var v = this.el.dom.value.trim();
20287 // if(v.length < 1){
20291 if(this.owner.fireEvent('beforepush', this, v) !== false){
20292 var d = (this.doc.body || this.doc.documentElement);
20294 this.cleanUpPaste();
20295 this.el.dom.value = d.innerHTML;
20296 this.owner.fireEvent('push', this, v);
20302 deferFocus : function(){
20303 this.focus.defer(10, this);
20307 focus : function(){
20308 if(this.win && !this.sourceEditMode){
20315 assignDocWin: function()
20317 var iframe = this.iframe;
20320 this.doc = iframe.contentWindow.document;
20321 this.win = iframe.contentWindow;
20323 // if (!Roo.get(this.frameId)) {
20326 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20327 // this.win = Roo.get(this.frameId).dom.contentWindow;
20329 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20333 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20334 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20339 initEditor : function(){
20340 //console.log("INIT EDITOR");
20341 this.assignDocWin();
20345 this.doc.designMode="on";
20347 this.doc.write(this.getDocMarkup());
20350 var dbody = (this.doc.body || this.doc.documentElement);
20351 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20352 // this copies styles from the containing element into thsi one..
20353 // not sure why we need all of this..
20354 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20356 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20357 //ss['background-attachment'] = 'fixed'; // w3c
20358 dbody.bgProperties = 'fixed'; // ie
20359 //Roo.DomHelper.applyStyles(dbody, ss);
20360 Roo.EventManager.on(this.doc, {
20361 //'mousedown': this.onEditorEvent,
20362 'mouseup': this.onEditorEvent,
20363 'dblclick': this.onEditorEvent,
20364 'click': this.onEditorEvent,
20365 'keyup': this.onEditorEvent,
20370 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20372 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20373 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20375 this.initialized = true;
20377 this.owner.fireEvent('initialize', this);
20382 onDestroy : function(){
20388 //for (var i =0; i < this.toolbars.length;i++) {
20389 // // fixme - ask toolbars for heights?
20390 // this.toolbars[i].onDestroy();
20393 //this.wrap.dom.innerHTML = '';
20394 //this.wrap.remove();
20399 onFirstFocus : function(){
20401 this.assignDocWin();
20404 this.activated = true;
20407 if(Roo.isGecko){ // prevent silly gecko errors
20409 var s = this.win.getSelection();
20410 if(!s.focusNode || s.focusNode.nodeType != 3){
20411 var r = s.getRangeAt(0);
20412 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20417 this.execCmd('useCSS', true);
20418 this.execCmd('styleWithCSS', false);
20421 this.owner.fireEvent('activate', this);
20425 adjustFont: function(btn){
20426 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20427 //if(Roo.isSafari){ // safari
20430 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20431 if(Roo.isSafari){ // safari
20432 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20433 v = (v < 10) ? 10 : v;
20434 v = (v > 48) ? 48 : v;
20435 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20440 v = Math.max(1, v+adjust);
20442 this.execCmd('FontSize', v );
20445 onEditorEvent : function(e)
20447 this.owner.fireEvent('editorevent', this, e);
20448 // this.updateToolbar();
20449 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20452 insertTag : function(tg)
20454 // could be a bit smarter... -> wrap the current selected tRoo..
20455 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20457 range = this.createRange(this.getSelection());
20458 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20459 wrappingNode.appendChild(range.extractContents());
20460 range.insertNode(wrappingNode);
20467 this.execCmd("formatblock", tg);
20471 insertText : function(txt)
20475 var range = this.createRange();
20476 range.deleteContents();
20477 //alert(Sender.getAttribute('label'));
20479 range.insertNode(this.doc.createTextNode(txt));
20485 * Executes a Midas editor command on the editor document and performs necessary focus and
20486 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20487 * @param {String} cmd The Midas command
20488 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20490 relayCmd : function(cmd, value){
20492 this.execCmd(cmd, value);
20493 this.owner.fireEvent('editorevent', this);
20494 //this.updateToolbar();
20495 this.owner.deferFocus();
20499 * Executes a Midas editor command directly on the editor document.
20500 * For visual commands, you should use {@link #relayCmd} instead.
20501 * <b>This should only be called after the editor is initialized.</b>
20502 * @param {String} cmd The Midas command
20503 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20505 execCmd : function(cmd, value){
20506 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20513 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20515 * @param {String} text | dom node..
20517 insertAtCursor : function(text)
20522 if(!this.activated){
20528 var r = this.doc.selection.createRange();
20539 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20543 // from jquery ui (MIT licenced)
20545 var win = this.win;
20547 if (win.getSelection && win.getSelection().getRangeAt) {
20548 range = win.getSelection().getRangeAt(0);
20549 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20550 range.insertNode(node);
20551 } else if (win.document.selection && win.document.selection.createRange) {
20552 // no firefox support
20553 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20554 win.document.selection.createRange().pasteHTML(txt);
20556 // no firefox support
20557 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20558 this.execCmd('InsertHTML', txt);
20567 mozKeyPress : function(e){
20569 var c = e.getCharCode(), cmd;
20572 c = String.fromCharCode(c).toLowerCase();
20586 this.cleanUpPaste.defer(100, this);
20594 e.preventDefault();
20602 fixKeys : function(){ // load time branching for fastest keydown performance
20604 return function(e){
20605 var k = e.getKey(), r;
20608 r = this.doc.selection.createRange();
20611 r.pasteHTML('    ');
20618 r = this.doc.selection.createRange();
20620 var target = r.parentElement();
20621 if(!target || target.tagName.toLowerCase() != 'li'){
20623 r.pasteHTML('<br />');
20629 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20630 this.cleanUpPaste.defer(100, this);
20636 }else if(Roo.isOpera){
20637 return function(e){
20638 var k = e.getKey();
20642 this.execCmd('InsertHTML','    ');
20645 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20646 this.cleanUpPaste.defer(100, this);
20651 }else if(Roo.isSafari){
20652 return function(e){
20653 var k = e.getKey();
20657 this.execCmd('InsertText','\t');
20661 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20662 this.cleanUpPaste.defer(100, this);
20670 getAllAncestors: function()
20672 var p = this.getSelectedNode();
20675 a.push(p); // push blank onto stack..
20676 p = this.getParentElement();
20680 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20684 a.push(this.doc.body);
20688 lastSelNode : false,
20691 getSelection : function()
20693 this.assignDocWin();
20694 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20697 getSelectedNode: function()
20699 // this may only work on Gecko!!!
20701 // should we cache this!!!!
20706 var range = this.createRange(this.getSelection()).cloneRange();
20709 var parent = range.parentElement();
20711 var testRange = range.duplicate();
20712 testRange.moveToElementText(parent);
20713 if (testRange.inRange(range)) {
20716 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20719 parent = parent.parentElement;
20724 // is ancestor a text element.
20725 var ac = range.commonAncestorContainer;
20726 if (ac.nodeType == 3) {
20727 ac = ac.parentNode;
20730 var ar = ac.childNodes;
20733 var other_nodes = [];
20734 var has_other_nodes = false;
20735 for (var i=0;i<ar.length;i++) {
20736 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20739 // fullly contained node.
20741 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20746 // probably selected..
20747 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20748 other_nodes.push(ar[i]);
20752 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20757 has_other_nodes = true;
20759 if (!nodes.length && other_nodes.length) {
20760 nodes= other_nodes;
20762 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20768 createRange: function(sel)
20770 // this has strange effects when using with
20771 // top toolbar - not sure if it's a great idea.
20772 //this.editor.contentWindow.focus();
20773 if (typeof sel != "undefined") {
20775 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20777 return this.doc.createRange();
20780 return this.doc.createRange();
20783 getParentElement: function()
20786 this.assignDocWin();
20787 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20789 var range = this.createRange(sel);
20792 var p = range.commonAncestorContainer;
20793 while (p.nodeType == 3) { // text node
20804 * Range intersection.. the hard stuff...
20808 * [ -- selected range --- ]
20812 * if end is before start or hits it. fail.
20813 * if start is after end or hits it fail.
20815 * if either hits (but other is outside. - then it's not
20821 // @see http://www.thismuchiknow.co.uk/?p=64.
20822 rangeIntersectsNode : function(range, node)
20824 var nodeRange = node.ownerDocument.createRange();
20826 nodeRange.selectNode(node);
20828 nodeRange.selectNodeContents(node);
20831 var rangeStartRange = range.cloneRange();
20832 rangeStartRange.collapse(true);
20834 var rangeEndRange = range.cloneRange();
20835 rangeEndRange.collapse(false);
20837 var nodeStartRange = nodeRange.cloneRange();
20838 nodeStartRange.collapse(true);
20840 var nodeEndRange = nodeRange.cloneRange();
20841 nodeEndRange.collapse(false);
20843 return rangeStartRange.compareBoundaryPoints(
20844 Range.START_TO_START, nodeEndRange) == -1 &&
20845 rangeEndRange.compareBoundaryPoints(
20846 Range.START_TO_START, nodeStartRange) == 1;
20850 rangeCompareNode : function(range, node)
20852 var nodeRange = node.ownerDocument.createRange();
20854 nodeRange.selectNode(node);
20856 nodeRange.selectNodeContents(node);
20860 range.collapse(true);
20862 nodeRange.collapse(true);
20864 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20865 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20867 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20869 var nodeIsBefore = ss == 1;
20870 var nodeIsAfter = ee == -1;
20872 if (nodeIsBefore && nodeIsAfter) {
20875 if (!nodeIsBefore && nodeIsAfter) {
20876 return 1; //right trailed.
20879 if (nodeIsBefore && !nodeIsAfter) {
20880 return 2; // left trailed.
20886 // private? - in a new class?
20887 cleanUpPaste : function()
20889 // cleans up the whole document..
20890 Roo.log('cleanuppaste');
20892 this.cleanUpChildren(this.doc.body);
20893 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20894 if (clean != this.doc.body.innerHTML) {
20895 this.doc.body.innerHTML = clean;
20900 cleanWordChars : function(input) {// change the chars to hex code
20901 var he = Roo.HtmlEditorCore;
20903 var output = input;
20904 Roo.each(he.swapCodes, function(sw) {
20905 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20907 output = output.replace(swapper, sw[1]);
20914 cleanUpChildren : function (n)
20916 if (!n.childNodes.length) {
20919 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20920 this.cleanUpChild(n.childNodes[i]);
20927 cleanUpChild : function (node)
20930 //console.log(node);
20931 if (node.nodeName == "#text") {
20932 // clean up silly Windows -- stuff?
20935 if (node.nodeName == "#comment") {
20936 node.parentNode.removeChild(node);
20937 // clean up silly Windows -- stuff?
20940 var lcname = node.tagName.toLowerCase();
20941 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20942 // whitelist of tags..
20944 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20946 node.parentNode.removeChild(node);
20951 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20953 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20954 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20956 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20957 // remove_keep_children = true;
20960 if (remove_keep_children) {
20961 this.cleanUpChildren(node);
20962 // inserts everything just before this node...
20963 while (node.childNodes.length) {
20964 var cn = node.childNodes[0];
20965 node.removeChild(cn);
20966 node.parentNode.insertBefore(cn, node);
20968 node.parentNode.removeChild(node);
20972 if (!node.attributes || !node.attributes.length) {
20973 this.cleanUpChildren(node);
20977 function cleanAttr(n,v)
20980 if (v.match(/^\./) || v.match(/^\//)) {
20983 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20986 if (v.match(/^#/)) {
20989 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20990 node.removeAttribute(n);
20994 var cwhite = this.cwhite;
20995 var cblack = this.cblack;
20997 function cleanStyle(n,v)
20999 if (v.match(/expression/)) { //XSS?? should we even bother..
21000 node.removeAttribute(n);
21004 var parts = v.split(/;/);
21007 Roo.each(parts, function(p) {
21008 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21012 var l = p.split(':').shift().replace(/\s+/g,'');
21013 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21015 if ( cwhite.length && cblack.indexOf(l) > -1) {
21016 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21017 //node.removeAttribute(n);
21021 // only allow 'c whitelisted system attributes'
21022 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21023 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21024 //node.removeAttribute(n);
21034 if (clean.length) {
21035 node.setAttribute(n, clean.join(';'));
21037 node.removeAttribute(n);
21043 for (var i = node.attributes.length-1; i > -1 ; i--) {
21044 var a = node.attributes[i];
21047 if (a.name.toLowerCase().substr(0,2)=='on') {
21048 node.removeAttribute(a.name);
21051 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21052 node.removeAttribute(a.name);
21055 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21056 cleanAttr(a.name,a.value); // fixme..
21059 if (a.name == 'style') {
21060 cleanStyle(a.name,a.value);
21063 /// clean up MS crap..
21064 // tecnically this should be a list of valid class'es..
21067 if (a.name == 'class') {
21068 if (a.value.match(/^Mso/)) {
21069 node.className = '';
21072 if (a.value.match(/body/)) {
21073 node.className = '';
21084 this.cleanUpChildren(node);
21090 * Clean up MS wordisms...
21092 cleanWord : function(node)
21097 this.cleanWord(this.doc.body);
21100 if (node.nodeName == "#text") {
21101 // clean up silly Windows -- stuff?
21104 if (node.nodeName == "#comment") {
21105 node.parentNode.removeChild(node);
21106 // clean up silly Windows -- stuff?
21110 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21111 node.parentNode.removeChild(node);
21115 // remove - but keep children..
21116 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21117 while (node.childNodes.length) {
21118 var cn = node.childNodes[0];
21119 node.removeChild(cn);
21120 node.parentNode.insertBefore(cn, node);
21122 node.parentNode.removeChild(node);
21123 this.iterateChildren(node, this.cleanWord);
21127 if (node.className.length) {
21129 var cn = node.className.split(/\W+/);
21131 Roo.each(cn, function(cls) {
21132 if (cls.match(/Mso[a-zA-Z]+/)) {
21137 node.className = cna.length ? cna.join(' ') : '';
21139 node.removeAttribute("class");
21143 if (node.hasAttribute("lang")) {
21144 node.removeAttribute("lang");
21147 if (node.hasAttribute("style")) {
21149 var styles = node.getAttribute("style").split(";");
21151 Roo.each(styles, function(s) {
21152 if (!s.match(/:/)) {
21155 var kv = s.split(":");
21156 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21159 // what ever is left... we allow.
21162 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21163 if (!nstyle.length) {
21164 node.removeAttribute('style');
21167 this.iterateChildren(node, this.cleanWord);
21173 * iterateChildren of a Node, calling fn each time, using this as the scole..
21174 * @param {DomNode} node node to iterate children of.
21175 * @param {Function} fn method of this class to call on each item.
21177 iterateChildren : function(node, fn)
21179 if (!node.childNodes.length) {
21182 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21183 fn.call(this, node.childNodes[i])
21189 * cleanTableWidths.
21191 * Quite often pasting from word etc.. results in tables with column and widths.
21192 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21195 cleanTableWidths : function(node)
21200 this.cleanTableWidths(this.doc.body);
21205 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21208 Roo.log(node.tagName);
21209 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21210 this.iterateChildren(node, this.cleanTableWidths);
21213 if (node.hasAttribute('width')) {
21214 node.removeAttribute('width');
21218 if (node.hasAttribute("style")) {
21221 var styles = node.getAttribute("style").split(";");
21223 Roo.each(styles, function(s) {
21224 if (!s.match(/:/)) {
21227 var kv = s.split(":");
21228 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21231 // what ever is left... we allow.
21234 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21235 if (!nstyle.length) {
21236 node.removeAttribute('style');
21240 this.iterateChildren(node, this.cleanTableWidths);
21248 domToHTML : function(currentElement, depth, nopadtext) {
21250 depth = depth || 0;
21251 nopadtext = nopadtext || false;
21253 if (!currentElement) {
21254 return this.domToHTML(this.doc.body);
21257 //Roo.log(currentElement);
21259 var allText = false;
21260 var nodeName = currentElement.nodeName;
21261 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21263 if (nodeName == '#text') {
21265 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21270 if (nodeName != 'BODY') {
21273 // Prints the node tagName, such as <A>, <IMG>, etc
21276 for(i = 0; i < currentElement.attributes.length;i++) {
21278 var aname = currentElement.attributes.item(i).name;
21279 if (!currentElement.attributes.item(i).value.length) {
21282 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21285 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21294 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21297 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21302 // Traverse the tree
21304 var currentElementChild = currentElement.childNodes.item(i);
21305 var allText = true;
21306 var innerHTML = '';
21308 while (currentElementChild) {
21309 // Formatting code (indent the tree so it looks nice on the screen)
21310 var nopad = nopadtext;
21311 if (lastnode == 'SPAN') {
21315 if (currentElementChild.nodeName == '#text') {
21316 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21317 toadd = nopadtext ? toadd : toadd.trim();
21318 if (!nopad && toadd.length > 80) {
21319 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21321 innerHTML += toadd;
21324 currentElementChild = currentElement.childNodes.item(i);
21330 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21332 // Recursively traverse the tree structure of the child node
21333 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21334 lastnode = currentElementChild.nodeName;
21336 currentElementChild=currentElement.childNodes.item(i);
21342 // The remaining code is mostly for formatting the tree
21343 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21348 ret+= "</"+tagName+">";
21354 applyBlacklists : function()
21356 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21357 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21361 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21362 if (b.indexOf(tag) > -1) {
21365 this.white.push(tag);
21369 Roo.each(w, function(tag) {
21370 if (b.indexOf(tag) > -1) {
21373 if (this.white.indexOf(tag) > -1) {
21376 this.white.push(tag);
21381 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21382 if (w.indexOf(tag) > -1) {
21385 this.black.push(tag);
21389 Roo.each(b, function(tag) {
21390 if (w.indexOf(tag) > -1) {
21393 if (this.black.indexOf(tag) > -1) {
21396 this.black.push(tag);
21401 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21402 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21406 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21407 if (b.indexOf(tag) > -1) {
21410 this.cwhite.push(tag);
21414 Roo.each(w, function(tag) {
21415 if (b.indexOf(tag) > -1) {
21418 if (this.cwhite.indexOf(tag) > -1) {
21421 this.cwhite.push(tag);
21426 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21427 if (w.indexOf(tag) > -1) {
21430 this.cblack.push(tag);
21434 Roo.each(b, function(tag) {
21435 if (w.indexOf(tag) > -1) {
21438 if (this.cblack.indexOf(tag) > -1) {
21441 this.cblack.push(tag);
21446 setStylesheets : function(stylesheets)
21448 if(typeof(stylesheets) == 'string'){
21449 Roo.get(this.iframe.contentDocument.head).createChild({
21451 rel : 'stylesheet',
21460 Roo.each(stylesheets, function(s) {
21465 Roo.get(_this.iframe.contentDocument.head).createChild({
21467 rel : 'stylesheet',
21476 removeStylesheets : function()
21480 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21485 // hide stuff that is not compatible
21499 * @event specialkey
21503 * @cfg {String} fieldClass @hide
21506 * @cfg {String} focusClass @hide
21509 * @cfg {String} autoCreate @hide
21512 * @cfg {String} inputType @hide
21515 * @cfg {String} invalidClass @hide
21518 * @cfg {String} invalidText @hide
21521 * @cfg {String} msgFx @hide
21524 * @cfg {String} validateOnBlur @hide
21528 Roo.HtmlEditorCore.white = [
21529 'area', 'br', 'img', 'input', 'hr', 'wbr',
21531 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21532 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21533 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21534 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21535 'table', 'ul', 'xmp',
21537 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21540 'dir', 'menu', 'ol', 'ul', 'dl',
21546 Roo.HtmlEditorCore.black = [
21547 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21549 'base', 'basefont', 'bgsound', 'blink', 'body',
21550 'frame', 'frameset', 'head', 'html', 'ilayer',
21551 'iframe', 'layer', 'link', 'meta', 'object',
21552 'script', 'style' ,'title', 'xml' // clean later..
21554 Roo.HtmlEditorCore.clean = [
21555 'script', 'style', 'title', 'xml'
21557 Roo.HtmlEditorCore.remove = [
21562 Roo.HtmlEditorCore.ablack = [
21566 Roo.HtmlEditorCore.aclean = [
21567 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21571 Roo.HtmlEditorCore.pwhite= [
21572 'http', 'https', 'mailto'
21575 // white listed style attributes.
21576 Roo.HtmlEditorCore.cwhite= [
21577 // 'text-align', /// default is to allow most things..
21583 // black listed style attributes.
21584 Roo.HtmlEditorCore.cblack= [
21585 // 'font-size' -- this can be set by the project
21589 Roo.HtmlEditorCore.swapCodes =[
21608 * @class Roo.bootstrap.HtmlEditor
21609 * @extends Roo.bootstrap.TextArea
21610 * Bootstrap HtmlEditor class
21613 * Create a new HtmlEditor
21614 * @param {Object} config The config object
21617 Roo.bootstrap.HtmlEditor = function(config){
21618 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21619 if (!this.toolbars) {
21620 this.toolbars = [];
21622 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21625 * @event initialize
21626 * Fires when the editor is fully initialized (including the iframe)
21627 * @param {HtmlEditor} this
21632 * Fires when the editor is first receives the focus. Any insertion must wait
21633 * until after this event.
21634 * @param {HtmlEditor} this
21638 * @event beforesync
21639 * Fires before the textarea is updated with content from the editor iframe. Return false
21640 * to cancel the sync.
21641 * @param {HtmlEditor} this
21642 * @param {String} html
21646 * @event beforepush
21647 * Fires before the iframe editor is updated with content from the textarea. Return false
21648 * to cancel the push.
21649 * @param {HtmlEditor} this
21650 * @param {String} html
21655 * Fires when the textarea is updated with content from the editor iframe.
21656 * @param {HtmlEditor} this
21657 * @param {String} html
21662 * Fires when the iframe editor is updated with content from the textarea.
21663 * @param {HtmlEditor} this
21664 * @param {String} html
21668 * @event editmodechange
21669 * Fires when the editor switches edit modes
21670 * @param {HtmlEditor} this
21671 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21673 editmodechange: true,
21675 * @event editorevent
21676 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21677 * @param {HtmlEditor} this
21681 * @event firstfocus
21682 * Fires when on first focus - needed by toolbars..
21683 * @param {HtmlEditor} this
21688 * Auto save the htmlEditor value as a file into Events
21689 * @param {HtmlEditor} this
21693 * @event savedpreview
21694 * preview the saved version of htmlEditor
21695 * @param {HtmlEditor} this
21702 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21706 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21711 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21716 * @cfg {Number} height (in pixels)
21720 * @cfg {Number} width (in pixels)
21725 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21728 stylesheets: false,
21733 // private properties
21734 validationEvent : false,
21736 initialized : false,
21739 onFocus : Roo.emptyFn,
21741 hideMode:'offsets',
21744 tbContainer : false,
21746 toolbarContainer :function() {
21747 return this.wrap.select('.x-html-editor-tb',true).first();
21751 * Protected method that will not generally be called directly. It
21752 * is called when the editor creates its toolbar. Override this method if you need to
21753 * add custom toolbar buttons.
21754 * @param {HtmlEditor} editor
21756 createToolbar : function(){
21758 Roo.log("create toolbars");
21760 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21761 this.toolbars[0].render(this.toolbarContainer());
21765 // if (!editor.toolbars || !editor.toolbars.length) {
21766 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21769 // for (var i =0 ; i < editor.toolbars.length;i++) {
21770 // editor.toolbars[i] = Roo.factory(
21771 // typeof(editor.toolbars[i]) == 'string' ?
21772 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21773 // Roo.bootstrap.HtmlEditor);
21774 // editor.toolbars[i].init(editor);
21780 onRender : function(ct, position)
21782 // Roo.log("Call onRender: " + this.xtype);
21784 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21786 this.wrap = this.inputEl().wrap({
21787 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21790 this.editorcore.onRender(ct, position);
21792 if (this.resizable) {
21793 this.resizeEl = new Roo.Resizable(this.wrap, {
21797 minHeight : this.height,
21798 height: this.height,
21799 handles : this.resizable,
21802 resize : function(r, w, h) {
21803 _t.onResize(w,h); // -something
21809 this.createToolbar(this);
21812 if(!this.width && this.resizable){
21813 this.setSize(this.wrap.getSize());
21815 if (this.resizeEl) {
21816 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21817 // should trigger onReize..
21823 onResize : function(w, h)
21825 Roo.log('resize: ' +w + ',' + h );
21826 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21830 if(this.inputEl() ){
21831 if(typeof w == 'number'){
21832 var aw = w - this.wrap.getFrameWidth('lr');
21833 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21836 if(typeof h == 'number'){
21837 var tbh = -11; // fixme it needs to tool bar size!
21838 for (var i =0; i < this.toolbars.length;i++) {
21839 // fixme - ask toolbars for heights?
21840 tbh += this.toolbars[i].el.getHeight();
21841 //if (this.toolbars[i].footer) {
21842 // tbh += this.toolbars[i].footer.el.getHeight();
21850 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21851 ah -= 5; // knock a few pixes off for look..
21852 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21856 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21857 this.editorcore.onResize(ew,eh);
21862 * Toggles the editor between standard and source edit mode.
21863 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21865 toggleSourceEdit : function(sourceEditMode)
21867 this.editorcore.toggleSourceEdit(sourceEditMode);
21869 if(this.editorcore.sourceEditMode){
21870 Roo.log('editor - showing textarea');
21873 // Roo.log(this.syncValue());
21875 this.inputEl().removeClass(['hide', 'x-hidden']);
21876 this.inputEl().dom.removeAttribute('tabIndex');
21877 this.inputEl().focus();
21879 Roo.log('editor - hiding textarea');
21881 // Roo.log(this.pushValue());
21884 this.inputEl().addClass(['hide', 'x-hidden']);
21885 this.inputEl().dom.setAttribute('tabIndex', -1);
21886 //this.deferFocus();
21889 if(this.resizable){
21890 this.setSize(this.wrap.getSize());
21893 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21896 // private (for BoxComponent)
21897 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21899 // private (for BoxComponent)
21900 getResizeEl : function(){
21904 // private (for BoxComponent)
21905 getPositionEl : function(){
21910 initEvents : function(){
21911 this.originalValue = this.getValue();
21915 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21918 // markInvalid : Roo.emptyFn,
21920 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21923 // clearInvalid : Roo.emptyFn,
21925 setValue : function(v){
21926 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21927 this.editorcore.pushValue();
21932 deferFocus : function(){
21933 this.focus.defer(10, this);
21937 focus : function(){
21938 this.editorcore.focus();
21944 onDestroy : function(){
21950 for (var i =0; i < this.toolbars.length;i++) {
21951 // fixme - ask toolbars for heights?
21952 this.toolbars[i].onDestroy();
21955 this.wrap.dom.innerHTML = '';
21956 this.wrap.remove();
21961 onFirstFocus : function(){
21962 //Roo.log("onFirstFocus");
21963 this.editorcore.onFirstFocus();
21964 for (var i =0; i < this.toolbars.length;i++) {
21965 this.toolbars[i].onFirstFocus();
21971 syncValue : function()
21973 this.editorcore.syncValue();
21976 pushValue : function()
21978 this.editorcore.pushValue();
21982 // hide stuff that is not compatible
21996 * @event specialkey
22000 * @cfg {String} fieldClass @hide
22003 * @cfg {String} focusClass @hide
22006 * @cfg {String} autoCreate @hide
22009 * @cfg {String} inputType @hide
22012 * @cfg {String} invalidClass @hide
22015 * @cfg {String} invalidText @hide
22018 * @cfg {String} msgFx @hide
22021 * @cfg {String} validateOnBlur @hide
22030 Roo.namespace('Roo.bootstrap.htmleditor');
22032 * @class Roo.bootstrap.HtmlEditorToolbar1
22037 new Roo.bootstrap.HtmlEditor({
22040 new Roo.bootstrap.HtmlEditorToolbar1({
22041 disable : { fonts: 1 , format: 1, ..., ... , ...],
22047 * @cfg {Object} disable List of elements to disable..
22048 * @cfg {Array} btns List of additional buttons.
22052 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22055 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22058 Roo.apply(this, config);
22060 // default disabled, based on 'good practice'..
22061 this.disable = this.disable || {};
22062 Roo.applyIf(this.disable, {
22065 specialElements : true
22067 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22069 this.editor = config.editor;
22070 this.editorcore = config.editor.editorcore;
22072 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22074 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22075 // dont call parent... till later.
22077 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22082 editorcore : false,
22087 "h1","h2","h3","h4","h5","h6",
22089 "abbr", "acronym", "address", "cite", "samp", "var",
22093 onRender : function(ct, position)
22095 // Roo.log("Call onRender: " + this.xtype);
22097 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22099 this.el.dom.style.marginBottom = '0';
22101 var editorcore = this.editorcore;
22102 var editor= this.editor;
22105 var btn = function(id,cmd , toggle, handler){
22107 var event = toggle ? 'toggle' : 'click';
22112 xns: Roo.bootstrap,
22115 enableToggle:toggle !== false,
22117 pressed : toggle ? false : null,
22120 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22121 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22130 xns: Roo.bootstrap,
22131 glyphicon : 'font',
22135 xns: Roo.bootstrap,
22139 Roo.each(this.formats, function(f) {
22140 style.menu.items.push({
22142 xns: Roo.bootstrap,
22143 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22148 editorcore.insertTag(this.tagname);
22155 children.push(style);
22158 btn('bold',false,true);
22159 btn('italic',false,true);
22160 btn('align-left', 'justifyleft',true);
22161 btn('align-center', 'justifycenter',true);
22162 btn('align-right' , 'justifyright',true);
22163 btn('link', false, false, function(btn) {
22164 //Roo.log("create link?");
22165 var url = prompt(this.createLinkText, this.defaultLinkValue);
22166 if(url && url != 'http:/'+'/'){
22167 this.editorcore.relayCmd('createlink', url);
22170 btn('list','insertunorderedlist',true);
22171 btn('pencil', false,true, function(btn){
22174 this.toggleSourceEdit(btn.pressed);
22180 xns: Roo.bootstrap,
22185 xns: Roo.bootstrap,
22190 cog.menu.items.push({
22192 xns: Roo.bootstrap,
22193 html : Clean styles,
22198 editorcore.insertTag(this.tagname);
22207 this.xtype = 'NavSimplebar';
22209 for(var i=0;i< children.length;i++) {
22211 this.buttons.add(this.addxtypeChild(children[i]));
22215 editor.on('editorevent', this.updateToolbar, this);
22217 onBtnClick : function(id)
22219 this.editorcore.relayCmd(id);
22220 this.editorcore.focus();
22224 * Protected method that will not generally be called directly. It triggers
22225 * a toolbar update by reading the markup state of the current selection in the editor.
22227 updateToolbar: function(){
22229 if(!this.editorcore.activated){
22230 this.editor.onFirstFocus(); // is this neeed?
22234 var btns = this.buttons;
22235 var doc = this.editorcore.doc;
22236 btns.get('bold').setActive(doc.queryCommandState('bold'));
22237 btns.get('italic').setActive(doc.queryCommandState('italic'));
22238 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22240 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22241 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22242 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22244 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22245 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22248 var ans = this.editorcore.getAllAncestors();
22249 if (this.formatCombo) {
22252 var store = this.formatCombo.store;
22253 this.formatCombo.setValue("");
22254 for (var i =0; i < ans.length;i++) {
22255 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22257 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22265 // hides menus... - so this cant be on a menu...
22266 Roo.bootstrap.MenuMgr.hideAll();
22268 Roo.bootstrap.MenuMgr.hideAll();
22269 //this.editorsyncValue();
22271 onFirstFocus: function() {
22272 this.buttons.each(function(item){
22276 toggleSourceEdit : function(sourceEditMode){
22279 if(sourceEditMode){
22280 Roo.log("disabling buttons");
22281 this.buttons.each( function(item){
22282 if(item.cmd != 'pencil'){
22288 Roo.log("enabling buttons");
22289 if(this.editorcore.initialized){
22290 this.buttons.each( function(item){
22296 Roo.log("calling toggole on editor");
22297 // tell the editor that it's been pressed..
22298 this.editor.toggleSourceEdit(sourceEditMode);
22308 * @class Roo.bootstrap.Table.AbstractSelectionModel
22309 * @extends Roo.util.Observable
22310 * Abstract base class for grid SelectionModels. It provides the interface that should be
22311 * implemented by descendant classes. This class should not be directly instantiated.
22314 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22315 this.locked = false;
22316 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22320 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22321 /** @ignore Called by the grid automatically. Do not call directly. */
22322 init : function(grid){
22328 * Locks the selections.
22331 this.locked = true;
22335 * Unlocks the selections.
22337 unlock : function(){
22338 this.locked = false;
22342 * Returns true if the selections are locked.
22343 * @return {Boolean}
22345 isLocked : function(){
22346 return this.locked;
22350 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22351 * @class Roo.bootstrap.Table.RowSelectionModel
22352 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22353 * It supports multiple selections and keyboard selection/navigation.
22355 * @param {Object} config
22358 Roo.bootstrap.Table.RowSelectionModel = function(config){
22359 Roo.apply(this, config);
22360 this.selections = new Roo.util.MixedCollection(false, function(o){
22365 this.lastActive = false;
22369 * @event selectionchange
22370 * Fires when the selection changes
22371 * @param {SelectionModel} this
22373 "selectionchange" : true,
22375 * @event afterselectionchange
22376 * Fires after the selection changes (eg. by key press or clicking)
22377 * @param {SelectionModel} this
22379 "afterselectionchange" : true,
22381 * @event beforerowselect
22382 * Fires when a row is selected being selected, return false to cancel.
22383 * @param {SelectionModel} this
22384 * @param {Number} rowIndex The selected index
22385 * @param {Boolean} keepExisting False if other selections will be cleared
22387 "beforerowselect" : true,
22390 * Fires when a row is selected.
22391 * @param {SelectionModel} this
22392 * @param {Number} rowIndex The selected index
22393 * @param {Roo.data.Record} r The record
22395 "rowselect" : true,
22397 * @event rowdeselect
22398 * Fires when a row is deselected.
22399 * @param {SelectionModel} this
22400 * @param {Number} rowIndex The selected index
22402 "rowdeselect" : true
22404 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22405 this.locked = false;
22409 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22411 * @cfg {Boolean} singleSelect
22412 * True to allow selection of only one row at a time (defaults to false)
22414 singleSelect : false,
22417 initEvents : function()
22420 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22421 this.grid.on("mousedown", this.handleMouseDown, this);
22422 }else{ // allow click to work like normal
22423 this.grid.on("rowclick", this.handleDragableRowClick, this);
22426 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22427 "up" : function(e){
22429 this.selectPrevious(e.shiftKey);
22430 }else if(this.last !== false && this.lastActive !== false){
22431 var last = this.last;
22432 this.selectRange(this.last, this.lastActive-1);
22433 this.grid.getView().focusRow(this.lastActive);
22434 if(last !== false){
22438 this.selectFirstRow();
22440 this.fireEvent("afterselectionchange", this);
22442 "down" : function(e){
22444 this.selectNext(e.shiftKey);
22445 }else if(this.last !== false && this.lastActive !== false){
22446 var last = this.last;
22447 this.selectRange(this.last, this.lastActive+1);
22448 this.grid.getView().focusRow(this.lastActive);
22449 if(last !== false){
22453 this.selectFirstRow();
22455 this.fireEvent("afterselectionchange", this);
22460 var view = this.grid.view;
22461 view.on("refresh", this.onRefresh, this);
22462 view.on("rowupdated", this.onRowUpdated, this);
22463 view.on("rowremoved", this.onRemove, this);
22467 onRefresh : function(){
22468 var ds = this.grid.dataSource, i, v = this.grid.view;
22469 var s = this.selections;
22470 s.each(function(r){
22471 if((i = ds.indexOfId(r.id)) != -1){
22480 onRemove : function(v, index, r){
22481 this.selections.remove(r);
22485 onRowUpdated : function(v, index, r){
22486 if(this.isSelected(r)){
22487 v.onRowSelect(index);
22493 * @param {Array} records The records to select
22494 * @param {Boolean} keepExisting (optional) True to keep existing selections
22496 selectRecords : function(records, keepExisting){
22498 this.clearSelections();
22500 var ds = this.grid.dataSource;
22501 for(var i = 0, len = records.length; i < len; i++){
22502 this.selectRow(ds.indexOf(records[i]), true);
22507 * Gets the number of selected rows.
22510 getCount : function(){
22511 return this.selections.length;
22515 * Selects the first row in the grid.
22517 selectFirstRow : function(){
22522 * Select the last row.
22523 * @param {Boolean} keepExisting (optional) True to keep existing selections
22525 selectLastRow : function(keepExisting){
22526 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22530 * Selects the row immediately following the last selected row.
22531 * @param {Boolean} keepExisting (optional) True to keep existing selections
22533 selectNext : function(keepExisting){
22534 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22535 this.selectRow(this.last+1, keepExisting);
22536 this.grid.getView().focusRow(this.last);
22541 * Selects the row that precedes the last selected row.
22542 * @param {Boolean} keepExisting (optional) True to keep existing selections
22544 selectPrevious : function(keepExisting){
22546 this.selectRow(this.last-1, keepExisting);
22547 this.grid.getView().focusRow(this.last);
22552 * Returns the selected records
22553 * @return {Array} Array of selected records
22555 getSelections : function(){
22556 return [].concat(this.selections.items);
22560 * Returns the first selected record.
22563 getSelected : function(){
22564 return this.selections.itemAt(0);
22569 * Clears all selections.
22571 clearSelections : function(fast){
22576 var ds = this.grid.dataSource;
22577 var s = this.selections;
22578 s.each(function(r){
22579 this.deselectRow(ds.indexOfId(r.id));
22583 this.selections.clear();
22590 * Selects all rows.
22592 selectAll : function(){
22596 this.selections.clear();
22597 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22598 this.selectRow(i, true);
22603 * Returns True if there is a selection.
22604 * @return {Boolean}
22606 hasSelection : function(){
22607 return this.selections.length > 0;
22611 * Returns True if the specified row is selected.
22612 * @param {Number/Record} record The record or index of the record to check
22613 * @return {Boolean}
22615 isSelected : function(index){
22616 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22617 return (r && this.selections.key(r.id) ? true : false);
22621 * Returns True if the specified record id is selected.
22622 * @param {String} id The id of record to check
22623 * @return {Boolean}
22625 isIdSelected : function(id){
22626 return (this.selections.key(id) ? true : false);
22630 handleMouseDown : function(e, t){
22631 var view = this.grid.getView(), rowIndex;
22632 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22635 if(e.shiftKey && this.last !== false){
22636 var last = this.last;
22637 this.selectRange(last, rowIndex, e.ctrlKey);
22638 this.last = last; // reset the last
22639 view.focusRow(rowIndex);
22641 var isSelected = this.isSelected(rowIndex);
22642 if(e.button !== 0 && isSelected){
22643 view.focusRow(rowIndex);
22644 }else if(e.ctrlKey && isSelected){
22645 this.deselectRow(rowIndex);
22646 }else if(!isSelected){
22647 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22648 view.focusRow(rowIndex);
22651 this.fireEvent("afterselectionchange", this);
22654 handleDragableRowClick : function(grid, rowIndex, e)
22656 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22657 this.selectRow(rowIndex, false);
22658 grid.view.focusRow(rowIndex);
22659 this.fireEvent("afterselectionchange", this);
22664 * Selects multiple rows.
22665 * @param {Array} rows Array of the indexes of the row to select
22666 * @param {Boolean} keepExisting (optional) True to keep existing selections
22668 selectRows : function(rows, keepExisting){
22670 this.clearSelections();
22672 for(var i = 0, len = rows.length; i < len; i++){
22673 this.selectRow(rows[i], true);
22678 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22679 * @param {Number} startRow The index of the first row in the range
22680 * @param {Number} endRow The index of the last row in the range
22681 * @param {Boolean} keepExisting (optional) True to retain existing selections
22683 selectRange : function(startRow, endRow, keepExisting){
22688 this.clearSelections();
22690 if(startRow <= endRow){
22691 for(var i = startRow; i <= endRow; i++){
22692 this.selectRow(i, true);
22695 for(var i = startRow; i >= endRow; i--){
22696 this.selectRow(i, true);
22702 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22703 * @param {Number} startRow The index of the first row in the range
22704 * @param {Number} endRow The index of the last row in the range
22706 deselectRange : function(startRow, endRow, preventViewNotify){
22710 for(var i = startRow; i <= endRow; i++){
22711 this.deselectRow(i, preventViewNotify);
22717 * @param {Number} row The index of the row to select
22718 * @param {Boolean} keepExisting (optional) True to keep existing selections
22720 selectRow : function(index, keepExisting, preventViewNotify){
22721 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22724 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22725 if(!keepExisting || this.singleSelect){
22726 this.clearSelections();
22728 var r = this.grid.dataSource.getAt(index);
22729 this.selections.add(r);
22730 this.last = this.lastActive = index;
22731 if(!preventViewNotify){
22732 this.grid.getView().onRowSelect(index);
22734 this.fireEvent("rowselect", this, index, r);
22735 this.fireEvent("selectionchange", this);
22741 * @param {Number} row The index of the row to deselect
22743 deselectRow : function(index, preventViewNotify){
22747 if(this.last == index){
22750 if(this.lastActive == index){
22751 this.lastActive = false;
22753 var r = this.grid.dataSource.getAt(index);
22754 this.selections.remove(r);
22755 if(!preventViewNotify){
22756 this.grid.getView().onRowDeselect(index);
22758 this.fireEvent("rowdeselect", this, index);
22759 this.fireEvent("selectionchange", this);
22763 restoreLast : function(){
22765 this.last = this._last;
22770 acceptsNav : function(row, col, cm){
22771 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22775 onEditorKey : function(field, e){
22776 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22781 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22783 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22785 }else if(k == e.ENTER && !e.ctrlKey){
22789 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22791 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22793 }else if(k == e.ESC){
22797 g.startEditing(newCell[0], newCell[1]);
22802 * Ext JS Library 1.1.1
22803 * Copyright(c) 2006-2007, Ext JS, LLC.
22805 * Originally Released Under LGPL - original licence link has changed is not relivant.
22808 * <script type="text/javascript">
22812 * @class Roo.bootstrap.PagingToolbar
22813 * @extends Roo.bootstrap.NavSimplebar
22814 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22816 * Create a new PagingToolbar
22817 * @param {Object} config The config object
22818 * @param {Roo.data.Store} store
22820 Roo.bootstrap.PagingToolbar = function(config)
22822 // old args format still supported... - xtype is prefered..
22823 // created from xtype...
22825 this.ds = config.dataSource;
22827 if (config.store && !this.ds) {
22828 this.store= Roo.factory(config.store, Roo.data);
22829 this.ds = this.store;
22830 this.ds.xmodule = this.xmodule || false;
22833 this.toolbarItems = [];
22834 if (config.items) {
22835 this.toolbarItems = config.items;
22838 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22843 this.bind(this.ds);
22846 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22850 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22852 * @cfg {Roo.data.Store} dataSource
22853 * The underlying data store providing the paged data
22856 * @cfg {String/HTMLElement/Element} container
22857 * container The id or element that will contain the toolbar
22860 * @cfg {Boolean} displayInfo
22861 * True to display the displayMsg (defaults to false)
22864 * @cfg {Number} pageSize
22865 * The number of records to display per page (defaults to 20)
22869 * @cfg {String} displayMsg
22870 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22872 displayMsg : 'Displaying {0} - {1} of {2}',
22874 * @cfg {String} emptyMsg
22875 * The message to display when no records are found (defaults to "No data to display")
22877 emptyMsg : 'No data to display',
22879 * Customizable piece of the default paging text (defaults to "Page")
22882 beforePageText : "Page",
22884 * Customizable piece of the default paging text (defaults to "of %0")
22887 afterPageText : "of {0}",
22889 * Customizable piece of the default paging text (defaults to "First Page")
22892 firstText : "First Page",
22894 * Customizable piece of the default paging text (defaults to "Previous Page")
22897 prevText : "Previous Page",
22899 * Customizable piece of the default paging text (defaults to "Next Page")
22902 nextText : "Next Page",
22904 * Customizable piece of the default paging text (defaults to "Last Page")
22907 lastText : "Last Page",
22909 * Customizable piece of the default paging text (defaults to "Refresh")
22912 refreshText : "Refresh",
22916 onRender : function(ct, position)
22918 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22919 this.navgroup.parentId = this.id;
22920 this.navgroup.onRender(this.el, null);
22921 // add the buttons to the navgroup
22923 if(this.displayInfo){
22924 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22925 this.displayEl = this.el.select('.x-paging-info', true).first();
22926 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22927 // this.displayEl = navel.el.select('span',true).first();
22933 Roo.each(_this.buttons, function(e){ // this might need to use render????
22934 Roo.factory(e).onRender(_this.el, null);
22938 Roo.each(_this.toolbarItems, function(e) {
22939 _this.navgroup.addItem(e);
22943 this.first = this.navgroup.addItem({
22944 tooltip: this.firstText,
22946 icon : 'fa fa-backward',
22948 preventDefault: true,
22949 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22952 this.prev = this.navgroup.addItem({
22953 tooltip: this.prevText,
22955 icon : 'fa fa-step-backward',
22957 preventDefault: true,
22958 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22960 //this.addSeparator();
22963 var field = this.navgroup.addItem( {
22965 cls : 'x-paging-position',
22967 html : this.beforePageText +
22968 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22969 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22972 this.field = field.el.select('input', true).first();
22973 this.field.on("keydown", this.onPagingKeydown, this);
22974 this.field.on("focus", function(){this.dom.select();});
22977 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22978 //this.field.setHeight(18);
22979 //this.addSeparator();
22980 this.next = this.navgroup.addItem({
22981 tooltip: this.nextText,
22983 html : ' <i class="fa fa-step-forward">',
22985 preventDefault: true,
22986 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22988 this.last = this.navgroup.addItem({
22989 tooltip: this.lastText,
22990 icon : 'fa fa-forward',
22993 preventDefault: true,
22994 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22996 //this.addSeparator();
22997 this.loading = this.navgroup.addItem({
22998 tooltip: this.refreshText,
22999 icon: 'fa fa-refresh',
23000 preventDefault: true,
23001 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23007 updateInfo : function(){
23008 if(this.displayEl){
23009 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23010 var msg = count == 0 ?
23014 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23016 this.displayEl.update(msg);
23021 onLoad : function(ds, r, o){
23022 this.cursor = o.params ? o.params.start : 0;
23023 var d = this.getPageData(),
23027 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23028 this.field.dom.value = ap;
23029 this.first.setDisabled(ap == 1);
23030 this.prev.setDisabled(ap == 1);
23031 this.next.setDisabled(ap == ps);
23032 this.last.setDisabled(ap == ps);
23033 this.loading.enable();
23038 getPageData : function(){
23039 var total = this.ds.getTotalCount();
23042 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23043 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23048 onLoadError : function(){
23049 this.loading.enable();
23053 onPagingKeydown : function(e){
23054 var k = e.getKey();
23055 var d = this.getPageData();
23057 var v = this.field.dom.value, pageNum;
23058 if(!v || isNaN(pageNum = parseInt(v, 10))){
23059 this.field.dom.value = d.activePage;
23062 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23063 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23066 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))
23068 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23069 this.field.dom.value = pageNum;
23070 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23073 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23075 var v = this.field.dom.value, pageNum;
23076 var increment = (e.shiftKey) ? 10 : 1;
23077 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23080 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23081 this.field.dom.value = d.activePage;
23084 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23086 this.field.dom.value = parseInt(v, 10) + increment;
23087 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23088 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23095 beforeLoad : function(){
23097 this.loading.disable();
23102 onClick : function(which){
23111 ds.load({params:{start: 0, limit: this.pageSize}});
23114 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23117 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23120 var total = ds.getTotalCount();
23121 var extra = total % this.pageSize;
23122 var lastStart = extra ? (total - extra) : total-this.pageSize;
23123 ds.load({params:{start: lastStart, limit: this.pageSize}});
23126 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23132 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23133 * @param {Roo.data.Store} store The data store to unbind
23135 unbind : function(ds){
23136 ds.un("beforeload", this.beforeLoad, this);
23137 ds.un("load", this.onLoad, this);
23138 ds.un("loadexception", this.onLoadError, this);
23139 ds.un("remove", this.updateInfo, this);
23140 ds.un("add", this.updateInfo, this);
23141 this.ds = undefined;
23145 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23146 * @param {Roo.data.Store} store The data store to bind
23148 bind : function(ds){
23149 ds.on("beforeload", this.beforeLoad, this);
23150 ds.on("load", this.onLoad, this);
23151 ds.on("loadexception", this.onLoadError, this);
23152 ds.on("remove", this.updateInfo, this);
23153 ds.on("add", this.updateInfo, this);
23164 * @class Roo.bootstrap.MessageBar
23165 * @extends Roo.bootstrap.Component
23166 * Bootstrap MessageBar class
23167 * @cfg {String} html contents of the MessageBar
23168 * @cfg {String} weight (info | success | warning | danger) default info
23169 * @cfg {String} beforeClass insert the bar before the given class
23170 * @cfg {Boolean} closable (true | false) default false
23171 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23174 * Create a new Element
23175 * @param {Object} config The config object
23178 Roo.bootstrap.MessageBar = function(config){
23179 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23182 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23188 beforeClass: 'bootstrap-sticky-wrap',
23190 getAutoCreate : function(){
23194 cls: 'alert alert-dismissable alert-' + this.weight,
23199 html: this.html || ''
23205 cfg.cls += ' alert-messages-fixed';
23219 onRender : function(ct, position)
23221 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23224 var cfg = Roo.apply({}, this.getAutoCreate());
23228 cfg.cls += ' ' + this.cls;
23231 cfg.style = this.style;
23233 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23235 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23238 this.el.select('>button.close').on('click', this.hide, this);
23244 if (!this.rendered) {
23250 this.fireEvent('show', this);
23256 if (!this.rendered) {
23262 this.fireEvent('hide', this);
23265 update : function()
23267 // var e = this.el.dom.firstChild;
23269 // if(this.closable){
23270 // e = e.nextSibling;
23273 // e.data = this.html || '';
23275 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23291 * @class Roo.bootstrap.Graph
23292 * @extends Roo.bootstrap.Component
23293 * Bootstrap Graph class
23297 @cfg {String} graphtype bar | vbar | pie
23298 @cfg {number} g_x coodinator | centre x (pie)
23299 @cfg {number} g_y coodinator | centre y (pie)
23300 @cfg {number} g_r radius (pie)
23301 @cfg {number} g_height height of the chart (respected by all elements in the set)
23302 @cfg {number} g_width width of the chart (respected by all elements in the set)
23303 @cfg {Object} title The title of the chart
23306 -opts (object) options for the chart
23308 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23309 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23311 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.
23312 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23314 o stretch (boolean)
23316 -opts (object) options for the pie
23319 o startAngle (number)
23320 o endAngle (number)
23324 * Create a new Input
23325 * @param {Object} config The config object
23328 Roo.bootstrap.Graph = function(config){
23329 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23335 * The img click event for the img.
23336 * @param {Roo.EventObject} e
23342 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23353 //g_colors: this.colors,
23360 getAutoCreate : function(){
23371 onRender : function(ct,position){
23374 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23376 if (typeof(Raphael) == 'undefined') {
23377 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23381 this.raphael = Raphael(this.el.dom);
23383 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23384 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23385 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23386 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23388 r.text(160, 10, "Single Series Chart").attr(txtattr);
23389 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23390 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23391 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23393 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23394 r.barchart(330, 10, 300, 220, data1);
23395 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23396 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23399 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23400 // r.barchart(30, 30, 560, 250, xdata, {
23401 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23402 // axis : "0 0 1 1",
23403 // axisxlabels : xdata
23404 // //yvalues : cols,
23407 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23409 // this.load(null,xdata,{
23410 // axis : "0 0 1 1",
23411 // axisxlabels : xdata
23416 load : function(graphtype,xdata,opts)
23418 this.raphael.clear();
23420 graphtype = this.graphtype;
23425 var r = this.raphael,
23426 fin = function () {
23427 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23429 fout = function () {
23430 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23432 pfin = function() {
23433 this.sector.stop();
23434 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23437 this.label[0].stop();
23438 this.label[0].attr({ r: 7.5 });
23439 this.label[1].attr({ "font-weight": 800 });
23442 pfout = function() {
23443 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23446 this.label[0].animate({ r: 5 }, 500, "bounce");
23447 this.label[1].attr({ "font-weight": 400 });
23453 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23456 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23459 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23460 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23462 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23469 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23474 setTitle: function(o)
23479 initEvents: function() {
23482 this.el.on('click', this.onClick, this);
23486 onClick : function(e)
23488 Roo.log('img onclick');
23489 this.fireEvent('click', this, e);
23501 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23504 * @class Roo.bootstrap.dash.NumberBox
23505 * @extends Roo.bootstrap.Component
23506 * Bootstrap NumberBox class
23507 * @cfg {String} headline Box headline
23508 * @cfg {String} content Box content
23509 * @cfg {String} icon Box icon
23510 * @cfg {String} footer Footer text
23511 * @cfg {String} fhref Footer href
23514 * Create a new NumberBox
23515 * @param {Object} config The config object
23519 Roo.bootstrap.dash.NumberBox = function(config){
23520 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23524 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23533 getAutoCreate : function(){
23537 cls : 'small-box ',
23545 cls : 'roo-headline',
23546 html : this.headline
23550 cls : 'roo-content',
23551 html : this.content
23565 cls : 'ion ' + this.icon
23574 cls : 'small-box-footer',
23575 href : this.fhref || '#',
23579 cfg.cn.push(footer);
23586 onRender : function(ct,position){
23587 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23594 setHeadline: function (value)
23596 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23599 setFooter: function (value, href)
23601 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23604 this.el.select('a.small-box-footer',true).first().attr('href', href);
23609 setContent: function (value)
23611 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23614 initEvents: function()
23628 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23631 * @class Roo.bootstrap.dash.TabBox
23632 * @extends Roo.bootstrap.Component
23633 * Bootstrap TabBox class
23634 * @cfg {String} title Title of the TabBox
23635 * @cfg {String} icon Icon of the TabBox
23636 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23637 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23640 * Create a new TabBox
23641 * @param {Object} config The config object
23645 Roo.bootstrap.dash.TabBox = function(config){
23646 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23651 * When a pane is added
23652 * @param {Roo.bootstrap.dash.TabPane} pane
23656 * @event activatepane
23657 * When a pane is activated
23658 * @param {Roo.bootstrap.dash.TabPane} pane
23660 "activatepane" : true
23668 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23673 tabScrollable : false,
23675 getChildContainer : function()
23677 return this.el.select('.tab-content', true).first();
23680 getAutoCreate : function(){
23684 cls: 'pull-left header',
23692 cls: 'fa ' + this.icon
23698 cls: 'nav nav-tabs pull-right',
23704 if(this.tabScrollable){
23711 cls: 'nav nav-tabs pull-right',
23722 cls: 'nav-tabs-custom',
23727 cls: 'tab-content no-padding',
23735 initEvents : function()
23737 //Roo.log('add add pane handler');
23738 this.on('addpane', this.onAddPane, this);
23741 * Updates the box title
23742 * @param {String} html to set the title to.
23744 setTitle : function(value)
23746 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23748 onAddPane : function(pane)
23750 this.panes.push(pane);
23751 //Roo.log('addpane');
23753 // tabs are rendere left to right..
23754 if(!this.showtabs){
23758 var ctr = this.el.select('.nav-tabs', true).first();
23761 var existing = ctr.select('.nav-tab',true);
23762 var qty = existing.getCount();;
23765 var tab = ctr.createChild({
23767 cls : 'nav-tab' + (qty ? '' : ' active'),
23775 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23778 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23780 pane.el.addClass('active');
23785 onTabClick : function(ev,un,ob,pane)
23787 //Roo.log('tab - prev default');
23788 ev.preventDefault();
23791 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23792 pane.tab.addClass('active');
23793 //Roo.log(pane.title);
23794 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23795 // technically we should have a deactivate event.. but maybe add later.
23796 // and it should not de-activate the selected tab...
23797 this.fireEvent('activatepane', pane);
23798 pane.el.addClass('active');
23799 pane.fireEvent('activate');
23804 getActivePane : function()
23807 Roo.each(this.panes, function(p) {
23808 if(p.el.hasClass('active')){
23829 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23831 * @class Roo.bootstrap.TabPane
23832 * @extends Roo.bootstrap.Component
23833 * Bootstrap TabPane class
23834 * @cfg {Boolean} active (false | true) Default false
23835 * @cfg {String} title title of panel
23839 * Create a new TabPane
23840 * @param {Object} config The config object
23843 Roo.bootstrap.dash.TabPane = function(config){
23844 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23850 * When a pane is activated
23851 * @param {Roo.bootstrap.dash.TabPane} pane
23858 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23863 // the tabBox that this is attached to.
23866 getAutoCreate : function()
23874 cfg.cls += ' active';
23879 initEvents : function()
23881 //Roo.log('trigger add pane handler');
23882 this.parent().fireEvent('addpane', this)
23886 * Updates the tab title
23887 * @param {String} html to set the title to.
23889 setTitle: function(str)
23895 this.tab.select('a', true).first().dom.innerHTML = str;
23912 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23915 * @class Roo.bootstrap.menu.Menu
23916 * @extends Roo.bootstrap.Component
23917 * Bootstrap Menu class - container for Menu
23918 * @cfg {String} html Text of the menu
23919 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23920 * @cfg {String} icon Font awesome icon
23921 * @cfg {String} pos Menu align to (top | bottom) default bottom
23925 * Create a new Menu
23926 * @param {Object} config The config object
23930 Roo.bootstrap.menu.Menu = function(config){
23931 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23935 * @event beforeshow
23936 * Fires before this menu is displayed
23937 * @param {Roo.bootstrap.menu.Menu} this
23941 * @event beforehide
23942 * Fires before this menu is hidden
23943 * @param {Roo.bootstrap.menu.Menu} this
23948 * Fires after this menu is displayed
23949 * @param {Roo.bootstrap.menu.Menu} this
23954 * Fires after this menu is hidden
23955 * @param {Roo.bootstrap.menu.Menu} this
23960 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23961 * @param {Roo.bootstrap.menu.Menu} this
23962 * @param {Roo.EventObject} e
23969 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23973 weight : 'default',
23978 getChildContainer : function() {
23979 if(this.isSubMenu){
23983 return this.el.select('ul.dropdown-menu', true).first();
23986 getAutoCreate : function()
23991 cls : 'roo-menu-text',
23999 cls : 'fa ' + this.icon
24010 cls : 'dropdown-button btn btn-' + this.weight,
24015 cls : 'dropdown-toggle btn btn-' + this.weight,
24025 cls : 'dropdown-menu'
24031 if(this.pos == 'top'){
24032 cfg.cls += ' dropup';
24035 if(this.isSubMenu){
24038 cls : 'dropdown-menu'
24045 onRender : function(ct, position)
24047 this.isSubMenu = ct.hasClass('dropdown-submenu');
24049 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24052 initEvents : function()
24054 if(this.isSubMenu){
24058 this.hidden = true;
24060 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24061 this.triggerEl.on('click', this.onTriggerPress, this);
24063 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24064 this.buttonEl.on('click', this.onClick, this);
24070 if(this.isSubMenu){
24074 return this.el.select('ul.dropdown-menu', true).first();
24077 onClick : function(e)
24079 this.fireEvent("click", this, e);
24082 onTriggerPress : function(e)
24084 if (this.isVisible()) {
24091 isVisible : function(){
24092 return !this.hidden;
24097 this.fireEvent("beforeshow", this);
24099 this.hidden = false;
24100 this.el.addClass('open');
24102 Roo.get(document).on("mouseup", this.onMouseUp, this);
24104 this.fireEvent("show", this);
24111 this.fireEvent("beforehide", this);
24113 this.hidden = true;
24114 this.el.removeClass('open');
24116 Roo.get(document).un("mouseup", this.onMouseUp);
24118 this.fireEvent("hide", this);
24121 onMouseUp : function()
24135 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24138 * @class Roo.bootstrap.menu.Item
24139 * @extends Roo.bootstrap.Component
24140 * Bootstrap MenuItem class
24141 * @cfg {Boolean} submenu (true | false) default false
24142 * @cfg {String} html text of the item
24143 * @cfg {String} href the link
24144 * @cfg {Boolean} disable (true | false) default false
24145 * @cfg {Boolean} preventDefault (true | false) default true
24146 * @cfg {String} icon Font awesome icon
24147 * @cfg {String} pos Submenu align to (left | right) default right
24151 * Create a new Item
24152 * @param {Object} config The config object
24156 Roo.bootstrap.menu.Item = function(config){
24157 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24161 * Fires when the mouse is hovering over this menu
24162 * @param {Roo.bootstrap.menu.Item} this
24163 * @param {Roo.EventObject} e
24168 * Fires when the mouse exits this menu
24169 * @param {Roo.bootstrap.menu.Item} this
24170 * @param {Roo.EventObject} e
24176 * The raw click event for the entire grid.
24177 * @param {Roo.EventObject} e
24183 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24188 preventDefault: true,
24193 getAutoCreate : function()
24198 cls : 'roo-menu-item-text',
24206 cls : 'fa ' + this.icon
24215 href : this.href || '#',
24222 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24226 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24228 if(this.pos == 'left'){
24229 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24236 initEvents : function()
24238 this.el.on('mouseover', this.onMouseOver, this);
24239 this.el.on('mouseout', this.onMouseOut, this);
24241 this.el.select('a', true).first().on('click', this.onClick, this);
24245 onClick : function(e)
24247 if(this.preventDefault){
24248 e.preventDefault();
24251 this.fireEvent("click", this, e);
24254 onMouseOver : function(e)
24256 if(this.submenu && this.pos == 'left'){
24257 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24260 this.fireEvent("mouseover", this, e);
24263 onMouseOut : function(e)
24265 this.fireEvent("mouseout", this, e);
24277 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24280 * @class Roo.bootstrap.menu.Separator
24281 * @extends Roo.bootstrap.Component
24282 * Bootstrap Separator class
24285 * Create a new Separator
24286 * @param {Object} config The config object
24290 Roo.bootstrap.menu.Separator = function(config){
24291 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24294 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24296 getAutoCreate : function(){
24317 * @class Roo.bootstrap.Tooltip
24318 * Bootstrap Tooltip class
24319 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24320 * to determine which dom element triggers the tooltip.
24322 * It needs to add support for additional attributes like tooltip-position
24325 * Create a new Toolti
24326 * @param {Object} config The config object
24329 Roo.bootstrap.Tooltip = function(config){
24330 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24333 Roo.apply(Roo.bootstrap.Tooltip, {
24335 * @function init initialize tooltip monitoring.
24339 currentTip : false,
24340 currentRegion : false,
24346 Roo.get(document).on('mouseover', this.enter ,this);
24347 Roo.get(document).on('mouseout', this.leave, this);
24350 this.currentTip = new Roo.bootstrap.Tooltip();
24353 enter : function(ev)
24355 var dom = ev.getTarget();
24357 //Roo.log(['enter',dom]);
24358 var el = Roo.fly(dom);
24359 if (this.currentEl) {
24361 //Roo.log(this.currentEl);
24362 //Roo.log(this.currentEl.contains(dom));
24363 if (this.currentEl == el) {
24366 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24372 if (this.currentTip.el) {
24373 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24377 if(!el || el.dom == document){
24383 // you can not look for children, as if el is the body.. then everythign is the child..
24384 if (!el.attr('tooltip')) { //
24385 if (!el.select("[tooltip]").elements.length) {
24388 // is the mouse over this child...?
24389 bindEl = el.select("[tooltip]").first();
24390 var xy = ev.getXY();
24391 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24392 //Roo.log("not in region.");
24395 //Roo.log("child element over..");
24398 this.currentEl = bindEl;
24399 this.currentTip.bind(bindEl);
24400 this.currentRegion = Roo.lib.Region.getRegion(dom);
24401 this.currentTip.enter();
24404 leave : function(ev)
24406 var dom = ev.getTarget();
24407 //Roo.log(['leave',dom]);
24408 if (!this.currentEl) {
24413 if (dom != this.currentEl.dom) {
24416 var xy = ev.getXY();
24417 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24420 // only activate leave if mouse cursor is outside... bounding box..
24425 if (this.currentTip) {
24426 this.currentTip.leave();
24428 //Roo.log('clear currentEl');
24429 this.currentEl = false;
24434 'left' : ['r-l', [-2,0], 'right'],
24435 'right' : ['l-r', [2,0], 'left'],
24436 'bottom' : ['t-b', [0,2], 'top'],
24437 'top' : [ 'b-t', [0,-2], 'bottom']
24443 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24448 delay : null, // can be { show : 300 , hide: 500}
24452 hoverState : null, //???
24454 placement : 'bottom',
24456 getAutoCreate : function(){
24463 cls : 'tooltip-arrow'
24466 cls : 'tooltip-inner'
24473 bind : function(el)
24479 enter : function () {
24481 if (this.timeout != null) {
24482 clearTimeout(this.timeout);
24485 this.hoverState = 'in';
24486 //Roo.log("enter - show");
24487 if (!this.delay || !this.delay.show) {
24492 this.timeout = setTimeout(function () {
24493 if (_t.hoverState == 'in') {
24496 }, this.delay.show);
24500 clearTimeout(this.timeout);
24502 this.hoverState = 'out';
24503 if (!this.delay || !this.delay.hide) {
24509 this.timeout = setTimeout(function () {
24510 //Roo.log("leave - timeout");
24512 if (_t.hoverState == 'out') {
24514 Roo.bootstrap.Tooltip.currentEl = false;
24522 this.render(document.body);
24525 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24527 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24529 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24531 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24533 var placement = typeof this.placement == 'function' ?
24534 this.placement.call(this, this.el, on_el) :
24537 var autoToken = /\s?auto?\s?/i;
24538 var autoPlace = autoToken.test(placement);
24540 placement = placement.replace(autoToken, '') || 'top';
24544 //this.el.setXY([0,0]);
24546 //this.el.dom.style.display='block';
24548 //this.el.appendTo(on_el);
24550 var p = this.getPosition();
24551 var box = this.el.getBox();
24557 var align = Roo.bootstrap.Tooltip.alignment[placement];
24559 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24561 if(placement == 'top' || placement == 'bottom'){
24563 placement = 'right';
24566 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24567 placement = 'left';
24570 var scroll = Roo.select('body', true).first().getScroll();
24572 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24578 align = Roo.bootstrap.Tooltip.alignment[placement];
24580 this.el.alignTo(this.bindEl, align[0],align[1]);
24581 //var arrow = this.el.select('.arrow',true).first();
24582 //arrow.set(align[2],
24584 this.el.addClass(placement);
24586 this.el.addClass('in fade');
24588 this.hoverState = null;
24590 if (this.el.hasClass('fade')) {
24601 //this.el.setXY([0,0]);
24602 this.el.removeClass('in');
24618 * @class Roo.bootstrap.LocationPicker
24619 * @extends Roo.bootstrap.Component
24620 * Bootstrap LocationPicker class
24621 * @cfg {Number} latitude Position when init default 0
24622 * @cfg {Number} longitude Position when init default 0
24623 * @cfg {Number} zoom default 15
24624 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24625 * @cfg {Boolean} mapTypeControl default false
24626 * @cfg {Boolean} disableDoubleClickZoom default false
24627 * @cfg {Boolean} scrollwheel default true
24628 * @cfg {Boolean} streetViewControl default false
24629 * @cfg {Number} radius default 0
24630 * @cfg {String} locationName
24631 * @cfg {Boolean} draggable default true
24632 * @cfg {Boolean} enableAutocomplete default false
24633 * @cfg {Boolean} enableReverseGeocode default true
24634 * @cfg {String} markerTitle
24637 * Create a new LocationPicker
24638 * @param {Object} config The config object
24642 Roo.bootstrap.LocationPicker = function(config){
24644 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24649 * Fires when the picker initialized.
24650 * @param {Roo.bootstrap.LocationPicker} this
24651 * @param {Google Location} location
24655 * @event positionchanged
24656 * Fires when the picker position changed.
24657 * @param {Roo.bootstrap.LocationPicker} this
24658 * @param {Google Location} location
24660 positionchanged : true,
24663 * Fires when the map resize.
24664 * @param {Roo.bootstrap.LocationPicker} this
24669 * Fires when the map show.
24670 * @param {Roo.bootstrap.LocationPicker} this
24675 * Fires when the map hide.
24676 * @param {Roo.bootstrap.LocationPicker} this
24681 * Fires when click the map.
24682 * @param {Roo.bootstrap.LocationPicker} this
24683 * @param {Map event} e
24687 * @event mapRightClick
24688 * Fires when right click the map.
24689 * @param {Roo.bootstrap.LocationPicker} this
24690 * @param {Map event} e
24692 mapRightClick : true,
24694 * @event markerClick
24695 * Fires when click the marker.
24696 * @param {Roo.bootstrap.LocationPicker} this
24697 * @param {Map event} e
24699 markerClick : true,
24701 * @event markerRightClick
24702 * Fires when right click the marker.
24703 * @param {Roo.bootstrap.LocationPicker} this
24704 * @param {Map event} e
24706 markerRightClick : true,
24708 * @event OverlayViewDraw
24709 * Fires when OverlayView Draw
24710 * @param {Roo.bootstrap.LocationPicker} this
24712 OverlayViewDraw : true,
24714 * @event OverlayViewOnAdd
24715 * Fires when OverlayView Draw
24716 * @param {Roo.bootstrap.LocationPicker} this
24718 OverlayViewOnAdd : true,
24720 * @event OverlayViewOnRemove
24721 * Fires when OverlayView Draw
24722 * @param {Roo.bootstrap.LocationPicker} this
24724 OverlayViewOnRemove : true,
24726 * @event OverlayViewShow
24727 * Fires when OverlayView Draw
24728 * @param {Roo.bootstrap.LocationPicker} this
24729 * @param {Pixel} cpx
24731 OverlayViewShow : true,
24733 * @event OverlayViewHide
24734 * Fires when OverlayView Draw
24735 * @param {Roo.bootstrap.LocationPicker} this
24737 OverlayViewHide : true,
24739 * @event loadexception
24740 * Fires when load google lib failed.
24741 * @param {Roo.bootstrap.LocationPicker} this
24743 loadexception : true
24748 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24750 gMapContext: false,
24756 mapTypeControl: false,
24757 disableDoubleClickZoom: false,
24759 streetViewControl: false,
24763 enableAutocomplete: false,
24764 enableReverseGeocode: true,
24767 getAutoCreate: function()
24772 cls: 'roo-location-picker'
24778 initEvents: function(ct, position)
24780 if(!this.el.getWidth() || this.isApplied()){
24784 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24789 initial: function()
24791 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24792 this.fireEvent('loadexception', this);
24796 if(!this.mapTypeId){
24797 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24800 this.gMapContext = this.GMapContext();
24802 this.initOverlayView();
24804 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24808 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24809 _this.setPosition(_this.gMapContext.marker.position);
24812 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24813 _this.fireEvent('mapClick', this, event);
24817 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24818 _this.fireEvent('mapRightClick', this, event);
24822 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24823 _this.fireEvent('markerClick', this, event);
24827 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24828 _this.fireEvent('markerRightClick', this, event);
24832 this.setPosition(this.gMapContext.location);
24834 this.fireEvent('initial', this, this.gMapContext.location);
24837 initOverlayView: function()
24841 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24845 _this.fireEvent('OverlayViewDraw', _this);
24850 _this.fireEvent('OverlayViewOnAdd', _this);
24853 onRemove: function()
24855 _this.fireEvent('OverlayViewOnRemove', _this);
24858 show: function(cpx)
24860 _this.fireEvent('OverlayViewShow', _this, cpx);
24865 _this.fireEvent('OverlayViewHide', _this);
24871 fromLatLngToContainerPixel: function(event)
24873 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24876 isApplied: function()
24878 return this.getGmapContext() == false ? false : true;
24881 getGmapContext: function()
24883 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24886 GMapContext: function()
24888 var position = new google.maps.LatLng(this.latitude, this.longitude);
24890 var _map = new google.maps.Map(this.el.dom, {
24893 mapTypeId: this.mapTypeId,
24894 mapTypeControl: this.mapTypeControl,
24895 disableDoubleClickZoom: this.disableDoubleClickZoom,
24896 scrollwheel: this.scrollwheel,
24897 streetViewControl: this.streetViewControl,
24898 locationName: this.locationName,
24899 draggable: this.draggable,
24900 enableAutocomplete: this.enableAutocomplete,
24901 enableReverseGeocode: this.enableReverseGeocode
24904 var _marker = new google.maps.Marker({
24905 position: position,
24907 title: this.markerTitle,
24908 draggable: this.draggable
24915 location: position,
24916 radius: this.radius,
24917 locationName: this.locationName,
24918 addressComponents: {
24919 formatted_address: null,
24920 addressLine1: null,
24921 addressLine2: null,
24923 streetNumber: null,
24927 stateOrProvince: null
24930 domContainer: this.el.dom,
24931 geodecoder: new google.maps.Geocoder()
24935 drawCircle: function(center, radius, options)
24937 if (this.gMapContext.circle != null) {
24938 this.gMapContext.circle.setMap(null);
24942 options = Roo.apply({}, options, {
24943 strokeColor: "#0000FF",
24944 strokeOpacity: .35,
24946 fillColor: "#0000FF",
24950 options.map = this.gMapContext.map;
24951 options.radius = radius;
24952 options.center = center;
24953 this.gMapContext.circle = new google.maps.Circle(options);
24954 return this.gMapContext.circle;
24960 setPosition: function(location)
24962 this.gMapContext.location = location;
24963 this.gMapContext.marker.setPosition(location);
24964 this.gMapContext.map.panTo(location);
24965 this.drawCircle(location, this.gMapContext.radius, {});
24969 if (this.gMapContext.settings.enableReverseGeocode) {
24970 this.gMapContext.geodecoder.geocode({
24971 latLng: this.gMapContext.location
24972 }, function(results, status) {
24974 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24975 _this.gMapContext.locationName = results[0].formatted_address;
24976 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24978 _this.fireEvent('positionchanged', this, location);
24985 this.fireEvent('positionchanged', this, location);
24990 google.maps.event.trigger(this.gMapContext.map, "resize");
24992 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24994 this.fireEvent('resize', this);
24997 setPositionByLatLng: function(latitude, longitude)
24999 this.setPosition(new google.maps.LatLng(latitude, longitude));
25002 getCurrentPosition: function()
25005 latitude: this.gMapContext.location.lat(),
25006 longitude: this.gMapContext.location.lng()
25010 getAddressName: function()
25012 return this.gMapContext.locationName;
25015 getAddressComponents: function()
25017 return this.gMapContext.addressComponents;
25020 address_component_from_google_geocode: function(address_components)
25024 for (var i = 0; i < address_components.length; i++) {
25025 var component = address_components[i];
25026 if (component.types.indexOf("postal_code") >= 0) {
25027 result.postalCode = component.short_name;
25028 } else if (component.types.indexOf("street_number") >= 0) {
25029 result.streetNumber = component.short_name;
25030 } else if (component.types.indexOf("route") >= 0) {
25031 result.streetName = component.short_name;
25032 } else if (component.types.indexOf("neighborhood") >= 0) {
25033 result.city = component.short_name;
25034 } else if (component.types.indexOf("locality") >= 0) {
25035 result.city = component.short_name;
25036 } else if (component.types.indexOf("sublocality") >= 0) {
25037 result.district = component.short_name;
25038 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25039 result.stateOrProvince = component.short_name;
25040 } else if (component.types.indexOf("country") >= 0) {
25041 result.country = component.short_name;
25045 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25046 result.addressLine2 = "";
25050 setZoomLevel: function(zoom)
25052 this.gMapContext.map.setZoom(zoom);
25065 this.fireEvent('show', this);
25076 this.fireEvent('hide', this);
25081 Roo.apply(Roo.bootstrap.LocationPicker, {
25083 OverlayView : function(map, options)
25085 options = options || {};
25099 * @class Roo.bootstrap.Alert
25100 * @extends Roo.bootstrap.Component
25101 * Bootstrap Alert class
25102 * @cfg {String} title The title of alert
25103 * @cfg {String} html The content of alert
25104 * @cfg {String} weight ( success | info | warning | danger )
25105 * @cfg {String} faicon font-awesomeicon
25108 * Create a new alert
25109 * @param {Object} config The config object
25113 Roo.bootstrap.Alert = function(config){
25114 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25118 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25125 getAutoCreate : function()
25134 cls : 'roo-alert-icon'
25139 cls : 'roo-alert-title',
25144 cls : 'roo-alert-text',
25151 cfg.cn[0].cls += ' fa ' + this.faicon;
25155 cfg.cls += ' alert-' + this.weight;
25161 initEvents: function()
25163 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25166 setTitle : function(str)
25168 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25171 setText : function(str)
25173 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25176 setWeight : function(weight)
25179 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25182 this.weight = weight;
25184 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25187 setIcon : function(icon)
25190 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25193 this.faicon = icon;
25195 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25216 * @class Roo.bootstrap.UploadCropbox
25217 * @extends Roo.bootstrap.Component
25218 * Bootstrap UploadCropbox class
25219 * @cfg {String} emptyText show when image has been loaded
25220 * @cfg {String} rotateNotify show when image too small to rotate
25221 * @cfg {Number} errorTimeout default 3000
25222 * @cfg {Number} minWidth default 300
25223 * @cfg {Number} minHeight default 300
25224 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25225 * @cfg {Boolean} isDocument (true|false) default false
25226 * @cfg {String} url action url
25227 * @cfg {String} paramName default 'imageUpload'
25228 * @cfg {String} method default POST
25229 * @cfg {Boolean} loadMask (true|false) default true
25230 * @cfg {Boolean} loadingText default 'Loading...'
25233 * Create a new UploadCropbox
25234 * @param {Object} config The config object
25237 Roo.bootstrap.UploadCropbox = function(config){
25238 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25242 * @event beforeselectfile
25243 * Fire before select file
25244 * @param {Roo.bootstrap.UploadCropbox} this
25246 "beforeselectfile" : true,
25249 * Fire after initEvent
25250 * @param {Roo.bootstrap.UploadCropbox} this
25255 * Fire after initEvent
25256 * @param {Roo.bootstrap.UploadCropbox} this
25257 * @param {String} data
25262 * Fire when preparing the file data
25263 * @param {Roo.bootstrap.UploadCropbox} this
25264 * @param {Object} file
25269 * Fire when get exception
25270 * @param {Roo.bootstrap.UploadCropbox} this
25271 * @param {XMLHttpRequest} xhr
25273 "exception" : true,
25275 * @event beforeloadcanvas
25276 * Fire before load the canvas
25277 * @param {Roo.bootstrap.UploadCropbox} this
25278 * @param {String} src
25280 "beforeloadcanvas" : true,
25283 * Fire when trash image
25284 * @param {Roo.bootstrap.UploadCropbox} this
25289 * Fire when download the image
25290 * @param {Roo.bootstrap.UploadCropbox} this
25294 * @event footerbuttonclick
25295 * Fire when footerbuttonclick
25296 * @param {Roo.bootstrap.UploadCropbox} this
25297 * @param {String} type
25299 "footerbuttonclick" : true,
25303 * @param {Roo.bootstrap.UploadCropbox} this
25308 * Fire when rotate the image
25309 * @param {Roo.bootstrap.UploadCropbox} this
25310 * @param {String} pos
25315 * Fire when inspect the file
25316 * @param {Roo.bootstrap.UploadCropbox} this
25317 * @param {Object} file
25322 * Fire when xhr upload the file
25323 * @param {Roo.bootstrap.UploadCropbox} this
25324 * @param {Object} data
25329 * Fire when arrange the file data
25330 * @param {Roo.bootstrap.UploadCropbox} this
25331 * @param {Object} formData
25336 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25339 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25341 emptyText : 'Click to upload image',
25342 rotateNotify : 'Image is too small to rotate',
25343 errorTimeout : 3000,
25357 cropType : 'image/jpeg',
25359 canvasLoaded : false,
25360 isDocument : false,
25362 paramName : 'imageUpload',
25364 loadingText : 'Loading...',
25367 getAutoCreate : function()
25371 cls : 'roo-upload-cropbox',
25375 cls : 'roo-upload-cropbox-selector',
25380 cls : 'roo-upload-cropbox-body',
25381 style : 'cursor:pointer',
25385 cls : 'roo-upload-cropbox-preview'
25389 cls : 'roo-upload-cropbox-thumb'
25393 cls : 'roo-upload-cropbox-empty-notify',
25394 html : this.emptyText
25398 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25399 html : this.rotateNotify
25405 cls : 'roo-upload-cropbox-footer',
25408 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25418 onRender : function(ct, position)
25420 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25422 if (this.buttons.length) {
25424 Roo.each(this.buttons, function(bb) {
25426 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25428 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25434 this.maskEl = this.el;
25438 initEvents : function()
25440 this.urlAPI = (window.createObjectURL && window) ||
25441 (window.URL && URL.revokeObjectURL && URL) ||
25442 (window.webkitURL && webkitURL);
25444 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25445 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25447 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25448 this.selectorEl.hide();
25450 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25451 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25453 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25454 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25455 this.thumbEl.hide();
25457 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25458 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25460 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25461 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25462 this.errorEl.hide();
25464 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25465 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25466 this.footerEl.hide();
25468 this.setThumbBoxSize();
25474 this.fireEvent('initial', this);
25481 window.addEventListener("resize", function() { _this.resize(); } );
25483 this.bodyEl.on('click', this.beforeSelectFile, this);
25486 this.bodyEl.on('touchstart', this.onTouchStart, this);
25487 this.bodyEl.on('touchmove', this.onTouchMove, this);
25488 this.bodyEl.on('touchend', this.onTouchEnd, this);
25492 this.bodyEl.on('mousedown', this.onMouseDown, this);
25493 this.bodyEl.on('mousemove', this.onMouseMove, this);
25494 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25495 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25496 Roo.get(document).on('mouseup', this.onMouseUp, this);
25499 this.selectorEl.on('change', this.onFileSelected, this);
25505 this.baseScale = 1;
25507 this.baseRotate = 1;
25508 this.dragable = false;
25509 this.pinching = false;
25512 this.cropData = false;
25513 this.notifyEl.dom.innerHTML = this.emptyText;
25515 this.selectorEl.dom.value = '';
25519 resize : function()
25521 if(this.fireEvent('resize', this) != false){
25522 this.setThumbBoxPosition();
25523 this.setCanvasPosition();
25527 onFooterButtonClick : function(e, el, o, type)
25530 case 'rotate-left' :
25531 this.onRotateLeft(e);
25533 case 'rotate-right' :
25534 this.onRotateRight(e);
25537 this.beforeSelectFile(e);
25552 this.fireEvent('footerbuttonclick', this, type);
25555 beforeSelectFile : function(e)
25557 e.preventDefault();
25559 if(this.fireEvent('beforeselectfile', this) != false){
25560 this.selectorEl.dom.click();
25564 onFileSelected : function(e)
25566 e.preventDefault();
25568 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25572 var file = this.selectorEl.dom.files[0];
25574 if(this.fireEvent('inspect', this, file) != false){
25575 this.prepare(file);
25580 trash : function(e)
25582 this.fireEvent('trash', this);
25585 download : function(e)
25587 this.fireEvent('download', this);
25590 loadCanvas : function(src)
25592 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25596 this.imageEl = document.createElement('img');
25600 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25602 this.imageEl.src = src;
25606 onLoadCanvas : function()
25608 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25609 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25611 this.bodyEl.un('click', this.beforeSelectFile, this);
25613 this.notifyEl.hide();
25614 this.thumbEl.show();
25615 this.footerEl.show();
25617 this.baseRotateLevel();
25619 if(this.isDocument){
25620 this.setThumbBoxSize();
25623 this.setThumbBoxPosition();
25625 this.baseScaleLevel();
25631 this.canvasLoaded = true;
25634 this.maskEl.unmask();
25639 setCanvasPosition : function()
25641 if(!this.canvasEl){
25645 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25646 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25648 this.previewEl.setLeft(pw);
25649 this.previewEl.setTop(ph);
25653 onMouseDown : function(e)
25657 this.dragable = true;
25658 this.pinching = false;
25660 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25661 this.dragable = false;
25665 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25666 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25670 onMouseMove : function(e)
25674 if(!this.canvasLoaded){
25678 if (!this.dragable){
25682 var minX = Math.ceil(this.thumbEl.getLeft(true));
25683 var minY = Math.ceil(this.thumbEl.getTop(true));
25685 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25686 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25688 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25689 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25691 x = x - this.mouseX;
25692 y = y - this.mouseY;
25694 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25695 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25697 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25698 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25700 this.previewEl.setLeft(bgX);
25701 this.previewEl.setTop(bgY);
25703 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25704 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25707 onMouseUp : function(e)
25711 this.dragable = false;
25714 onMouseWheel : function(e)
25718 this.startScale = this.scale;
25720 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25722 if(!this.zoomable()){
25723 this.scale = this.startScale;
25732 zoomable : function()
25734 var minScale = this.thumbEl.getWidth() / this.minWidth;
25736 if(this.minWidth < this.minHeight){
25737 minScale = this.thumbEl.getHeight() / this.minHeight;
25740 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25741 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25745 (this.rotate == 0 || this.rotate == 180) &&
25747 width > this.imageEl.OriginWidth ||
25748 height > this.imageEl.OriginHeight ||
25749 (width < this.minWidth && height < this.minHeight)
25757 (this.rotate == 90 || this.rotate == 270) &&
25759 width > this.imageEl.OriginWidth ||
25760 height > this.imageEl.OriginHeight ||
25761 (width < this.minHeight && height < this.minWidth)
25768 !this.isDocument &&
25769 (this.rotate == 0 || this.rotate == 180) &&
25771 width < this.minWidth ||
25772 width > this.imageEl.OriginWidth ||
25773 height < this.minHeight ||
25774 height > this.imageEl.OriginHeight
25781 !this.isDocument &&
25782 (this.rotate == 90 || this.rotate == 270) &&
25784 width < this.minHeight ||
25785 width > this.imageEl.OriginWidth ||
25786 height < this.minWidth ||
25787 height > this.imageEl.OriginHeight
25797 onRotateLeft : function(e)
25799 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25801 var minScale = this.thumbEl.getWidth() / this.minWidth;
25803 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25804 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25806 this.startScale = this.scale;
25808 while (this.getScaleLevel() < minScale){
25810 this.scale = this.scale + 1;
25812 if(!this.zoomable()){
25817 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25818 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25823 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25830 this.scale = this.startScale;
25832 this.onRotateFail();
25837 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25839 if(this.isDocument){
25840 this.setThumbBoxSize();
25841 this.setThumbBoxPosition();
25842 this.setCanvasPosition();
25847 this.fireEvent('rotate', this, 'left');
25851 onRotateRight : function(e)
25853 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25855 var minScale = this.thumbEl.getWidth() / this.minWidth;
25857 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25858 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25860 this.startScale = this.scale;
25862 while (this.getScaleLevel() < minScale){
25864 this.scale = this.scale + 1;
25866 if(!this.zoomable()){
25871 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25872 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25877 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25884 this.scale = this.startScale;
25886 this.onRotateFail();
25891 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25893 if(this.isDocument){
25894 this.setThumbBoxSize();
25895 this.setThumbBoxPosition();
25896 this.setCanvasPosition();
25901 this.fireEvent('rotate', this, 'right');
25904 onRotateFail : function()
25906 this.errorEl.show(true);
25910 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25915 this.previewEl.dom.innerHTML = '';
25917 var canvasEl = document.createElement("canvas");
25919 var contextEl = canvasEl.getContext("2d");
25921 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25922 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25923 var center = this.imageEl.OriginWidth / 2;
25925 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25926 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25927 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25928 center = this.imageEl.OriginHeight / 2;
25931 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25933 contextEl.translate(center, center);
25934 contextEl.rotate(this.rotate * Math.PI / 180);
25936 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25938 this.canvasEl = document.createElement("canvas");
25940 this.contextEl = this.canvasEl.getContext("2d");
25942 switch (this.rotate) {
25945 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25946 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25948 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25953 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25954 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25956 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25957 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);
25961 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25966 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25967 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25969 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25970 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);
25974 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);
25979 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25980 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25982 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25983 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25987 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);
25994 this.previewEl.appendChild(this.canvasEl);
25996 this.setCanvasPosition();
26001 if(!this.canvasLoaded){
26005 var imageCanvas = document.createElement("canvas");
26007 var imageContext = imageCanvas.getContext("2d");
26009 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26010 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26012 var center = imageCanvas.width / 2;
26014 imageContext.translate(center, center);
26016 imageContext.rotate(this.rotate * Math.PI / 180);
26018 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26020 var canvas = document.createElement("canvas");
26022 var context = canvas.getContext("2d");
26024 canvas.width = this.minWidth;
26025 canvas.height = this.minHeight;
26027 switch (this.rotate) {
26030 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26031 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26033 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26034 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26036 var targetWidth = this.minWidth - 2 * x;
26037 var targetHeight = this.minHeight - 2 * y;
26041 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26042 scale = targetWidth / width;
26045 if(x > 0 && y == 0){
26046 scale = targetHeight / height;
26049 if(x > 0 && y > 0){
26050 scale = targetWidth / width;
26052 if(width < height){
26053 scale = targetHeight / height;
26057 context.scale(scale, scale);
26059 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26060 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26062 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26063 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26065 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26070 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26071 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26073 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26074 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26076 var targetWidth = this.minWidth - 2 * x;
26077 var targetHeight = this.minHeight - 2 * y;
26081 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26082 scale = targetWidth / width;
26085 if(x > 0 && y == 0){
26086 scale = targetHeight / height;
26089 if(x > 0 && y > 0){
26090 scale = targetWidth / width;
26092 if(width < height){
26093 scale = targetHeight / height;
26097 context.scale(scale, scale);
26099 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26100 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26102 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26103 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26105 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26107 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26112 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26113 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26115 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26116 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26118 var targetWidth = this.minWidth - 2 * x;
26119 var targetHeight = this.minHeight - 2 * y;
26123 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26124 scale = targetWidth / width;
26127 if(x > 0 && y == 0){
26128 scale = targetHeight / height;
26131 if(x > 0 && y > 0){
26132 scale = targetWidth / width;
26134 if(width < height){
26135 scale = targetHeight / height;
26139 context.scale(scale, scale);
26141 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26142 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26144 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26145 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26147 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26148 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26150 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26155 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26156 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26158 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26159 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26161 var targetWidth = this.minWidth - 2 * x;
26162 var targetHeight = this.minHeight - 2 * y;
26166 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26167 scale = targetWidth / width;
26170 if(x > 0 && y == 0){
26171 scale = targetHeight / height;
26174 if(x > 0 && y > 0){
26175 scale = targetWidth / width;
26177 if(width < height){
26178 scale = targetHeight / height;
26182 context.scale(scale, scale);
26184 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26185 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26187 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26188 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26190 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26192 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26199 this.cropData = canvas.toDataURL(this.cropType);
26201 if(this.fireEvent('crop', this, this.cropData) !== false){
26202 this.process(this.file, this.cropData);
26209 setThumbBoxSize : function()
26213 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26214 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26215 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26217 this.minWidth = width;
26218 this.minHeight = height;
26220 if(this.rotate == 90 || this.rotate == 270){
26221 this.minWidth = height;
26222 this.minHeight = width;
26227 width = Math.ceil(this.minWidth * height / this.minHeight);
26229 if(this.minWidth > this.minHeight){
26231 height = Math.ceil(this.minHeight * width / this.minWidth);
26234 this.thumbEl.setStyle({
26235 width : width + 'px',
26236 height : height + 'px'
26243 setThumbBoxPosition : function()
26245 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26246 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26248 this.thumbEl.setLeft(x);
26249 this.thumbEl.setTop(y);
26253 baseRotateLevel : function()
26255 this.baseRotate = 1;
26258 typeof(this.exif) != 'undefined' &&
26259 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26260 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26262 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26265 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26269 baseScaleLevel : function()
26273 if(this.isDocument){
26275 if(this.baseRotate == 6 || this.baseRotate == 8){
26277 height = this.thumbEl.getHeight();
26278 this.baseScale = height / this.imageEl.OriginWidth;
26280 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26281 width = this.thumbEl.getWidth();
26282 this.baseScale = width / this.imageEl.OriginHeight;
26288 height = this.thumbEl.getHeight();
26289 this.baseScale = height / this.imageEl.OriginHeight;
26291 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26292 width = this.thumbEl.getWidth();
26293 this.baseScale = width / this.imageEl.OriginWidth;
26299 if(this.baseRotate == 6 || this.baseRotate == 8){
26301 width = this.thumbEl.getHeight();
26302 this.baseScale = width / this.imageEl.OriginHeight;
26304 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26305 height = this.thumbEl.getWidth();
26306 this.baseScale = height / this.imageEl.OriginHeight;
26309 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26310 height = this.thumbEl.getWidth();
26311 this.baseScale = height / this.imageEl.OriginHeight;
26313 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26314 width = this.thumbEl.getHeight();
26315 this.baseScale = width / this.imageEl.OriginWidth;
26322 width = this.thumbEl.getWidth();
26323 this.baseScale = width / this.imageEl.OriginWidth;
26325 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26326 height = this.thumbEl.getHeight();
26327 this.baseScale = height / this.imageEl.OriginHeight;
26330 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26332 height = this.thumbEl.getHeight();
26333 this.baseScale = height / this.imageEl.OriginHeight;
26335 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26336 width = this.thumbEl.getWidth();
26337 this.baseScale = width / this.imageEl.OriginWidth;
26345 getScaleLevel : function()
26347 return this.baseScale * Math.pow(1.1, this.scale);
26350 onTouchStart : function(e)
26352 if(!this.canvasLoaded){
26353 this.beforeSelectFile(e);
26357 var touches = e.browserEvent.touches;
26363 if(touches.length == 1){
26364 this.onMouseDown(e);
26368 if(touches.length != 2){
26374 for(var i = 0, finger; finger = touches[i]; i++){
26375 coords.push(finger.pageX, finger.pageY);
26378 var x = Math.pow(coords[0] - coords[2], 2);
26379 var y = Math.pow(coords[1] - coords[3], 2);
26381 this.startDistance = Math.sqrt(x + y);
26383 this.startScale = this.scale;
26385 this.pinching = true;
26386 this.dragable = false;
26390 onTouchMove : function(e)
26392 if(!this.pinching && !this.dragable){
26396 var touches = e.browserEvent.touches;
26403 this.onMouseMove(e);
26409 for(var i = 0, finger; finger = touches[i]; i++){
26410 coords.push(finger.pageX, finger.pageY);
26413 var x = Math.pow(coords[0] - coords[2], 2);
26414 var y = Math.pow(coords[1] - coords[3], 2);
26416 this.endDistance = Math.sqrt(x + y);
26418 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26420 if(!this.zoomable()){
26421 this.scale = this.startScale;
26429 onTouchEnd : function(e)
26431 this.pinching = false;
26432 this.dragable = false;
26436 process : function(file, crop)
26439 this.maskEl.mask(this.loadingText);
26442 this.xhr = new XMLHttpRequest();
26444 file.xhr = this.xhr;
26446 this.xhr.open(this.method, this.url, true);
26449 "Accept": "application/json",
26450 "Cache-Control": "no-cache",
26451 "X-Requested-With": "XMLHttpRequest"
26454 for (var headerName in headers) {
26455 var headerValue = headers[headerName];
26457 this.xhr.setRequestHeader(headerName, headerValue);
26463 this.xhr.onload = function()
26465 _this.xhrOnLoad(_this.xhr);
26468 this.xhr.onerror = function()
26470 _this.xhrOnError(_this.xhr);
26473 var formData = new FormData();
26475 formData.append('returnHTML', 'NO');
26478 formData.append('crop', crop);
26481 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26482 formData.append(this.paramName, file, file.name);
26485 if(typeof(file.filename) != 'undefined'){
26486 formData.append('filename', file.filename);
26489 if(typeof(file.mimetype) != 'undefined'){
26490 formData.append('mimetype', file.mimetype);
26493 if(this.fireEvent('arrange', this, formData) != false){
26494 this.xhr.send(formData);
26498 xhrOnLoad : function(xhr)
26501 this.maskEl.unmask();
26504 if (xhr.readyState !== 4) {
26505 this.fireEvent('exception', this, xhr);
26509 var response = Roo.decode(xhr.responseText);
26511 if(!response.success){
26512 this.fireEvent('exception', this, xhr);
26516 var response = Roo.decode(xhr.responseText);
26518 this.fireEvent('upload', this, response);
26522 xhrOnError : function()
26525 this.maskEl.unmask();
26528 Roo.log('xhr on error');
26530 var response = Roo.decode(xhr.responseText);
26536 prepare : function(file)
26539 this.maskEl.mask(this.loadingText);
26545 if(typeof(file) === 'string'){
26546 this.loadCanvas(file);
26550 if(!file || !this.urlAPI){
26555 this.cropType = file.type;
26559 if(this.fireEvent('prepare', this, this.file) != false){
26561 var reader = new FileReader();
26563 reader.onload = function (e) {
26564 if (e.target.error) {
26565 Roo.log(e.target.error);
26569 var buffer = e.target.result,
26570 dataView = new DataView(buffer),
26572 maxOffset = dataView.byteLength - 4,
26576 if (dataView.getUint16(0) === 0xffd8) {
26577 while (offset < maxOffset) {
26578 markerBytes = dataView.getUint16(offset);
26580 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26581 markerLength = dataView.getUint16(offset + 2) + 2;
26582 if (offset + markerLength > dataView.byteLength) {
26583 Roo.log('Invalid meta data: Invalid segment size.');
26587 if(markerBytes == 0xffe1){
26588 _this.parseExifData(
26595 offset += markerLength;
26605 var url = _this.urlAPI.createObjectURL(_this.file);
26607 _this.loadCanvas(url);
26612 reader.readAsArrayBuffer(this.file);
26618 parseExifData : function(dataView, offset, length)
26620 var tiffOffset = offset + 10,
26624 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26625 // No Exif data, might be XMP data instead
26629 // Check for the ASCII code for "Exif" (0x45786966):
26630 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26631 // No Exif data, might be XMP data instead
26634 if (tiffOffset + 8 > dataView.byteLength) {
26635 Roo.log('Invalid Exif data: Invalid segment size.');
26638 // Check for the two null bytes:
26639 if (dataView.getUint16(offset + 8) !== 0x0000) {
26640 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26643 // Check the byte alignment:
26644 switch (dataView.getUint16(tiffOffset)) {
26646 littleEndian = true;
26649 littleEndian = false;
26652 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26655 // Check for the TIFF tag marker (0x002A):
26656 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26657 Roo.log('Invalid Exif data: Missing TIFF marker.');
26660 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26661 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26663 this.parseExifTags(
26666 tiffOffset + dirOffset,
26671 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26676 if (dirOffset + 6 > dataView.byteLength) {
26677 Roo.log('Invalid Exif data: Invalid directory offset.');
26680 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26681 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26682 if (dirEndOffset + 4 > dataView.byteLength) {
26683 Roo.log('Invalid Exif data: Invalid directory size.');
26686 for (i = 0; i < tagsNumber; i += 1) {
26690 dirOffset + 2 + 12 * i, // tag offset
26694 // Return the offset to the next directory:
26695 return dataView.getUint32(dirEndOffset, littleEndian);
26698 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26700 var tag = dataView.getUint16(offset, littleEndian);
26702 this.exif[tag] = this.getExifValue(
26706 dataView.getUint16(offset + 2, littleEndian), // tag type
26707 dataView.getUint32(offset + 4, littleEndian), // tag length
26712 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26714 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26723 Roo.log('Invalid Exif data: Invalid tag type.');
26727 tagSize = tagType.size * length;
26728 // Determine if the value is contained in the dataOffset bytes,
26729 // or if the value at the dataOffset is a pointer to the actual data:
26730 dataOffset = tagSize > 4 ?
26731 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26732 if (dataOffset + tagSize > dataView.byteLength) {
26733 Roo.log('Invalid Exif data: Invalid data offset.');
26736 if (length === 1) {
26737 return tagType.getValue(dataView, dataOffset, littleEndian);
26740 for (i = 0; i < length; i += 1) {
26741 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26744 if (tagType.ascii) {
26746 // Concatenate the chars:
26747 for (i = 0; i < values.length; i += 1) {
26749 // Ignore the terminating NULL byte(s):
26750 if (c === '\u0000') {
26762 Roo.apply(Roo.bootstrap.UploadCropbox, {
26764 'Orientation': 0x0112
26768 1: 0, //'top-left',
26770 3: 180, //'bottom-right',
26771 // 4: 'bottom-left',
26773 6: 90, //'right-top',
26774 // 7: 'right-bottom',
26775 8: 270 //'left-bottom'
26779 // byte, 8-bit unsigned int:
26781 getValue: function (dataView, dataOffset) {
26782 return dataView.getUint8(dataOffset);
26786 // ascii, 8-bit byte:
26788 getValue: function (dataView, dataOffset) {
26789 return String.fromCharCode(dataView.getUint8(dataOffset));
26794 // short, 16 bit int:
26796 getValue: function (dataView, dataOffset, littleEndian) {
26797 return dataView.getUint16(dataOffset, littleEndian);
26801 // long, 32 bit int:
26803 getValue: function (dataView, dataOffset, littleEndian) {
26804 return dataView.getUint32(dataOffset, littleEndian);
26808 // rational = two long values, first is numerator, second is denominator:
26810 getValue: function (dataView, dataOffset, littleEndian) {
26811 return dataView.getUint32(dataOffset, littleEndian) /
26812 dataView.getUint32(dataOffset + 4, littleEndian);
26816 // slong, 32 bit signed int:
26818 getValue: function (dataView, dataOffset, littleEndian) {
26819 return dataView.getInt32(dataOffset, littleEndian);
26823 // srational, two slongs, first is numerator, second is denominator:
26825 getValue: function (dataView, dataOffset, littleEndian) {
26826 return dataView.getInt32(dataOffset, littleEndian) /
26827 dataView.getInt32(dataOffset + 4, littleEndian);
26837 cls : 'btn-group roo-upload-cropbox-rotate-left',
26838 action : 'rotate-left',
26842 cls : 'btn btn-default',
26843 html : '<i class="fa fa-undo"></i>'
26849 cls : 'btn-group roo-upload-cropbox-picture',
26850 action : 'picture',
26854 cls : 'btn btn-default',
26855 html : '<i class="fa fa-picture-o"></i>'
26861 cls : 'btn-group roo-upload-cropbox-rotate-right',
26862 action : 'rotate-right',
26866 cls : 'btn btn-default',
26867 html : '<i class="fa fa-repeat"></i>'
26875 cls : 'btn-group roo-upload-cropbox-rotate-left',
26876 action : 'rotate-left',
26880 cls : 'btn btn-default',
26881 html : '<i class="fa fa-undo"></i>'
26887 cls : 'btn-group roo-upload-cropbox-download',
26888 action : 'download',
26892 cls : 'btn btn-default',
26893 html : '<i class="fa fa-download"></i>'
26899 cls : 'btn-group roo-upload-cropbox-crop',
26904 cls : 'btn btn-default',
26905 html : '<i class="fa fa-crop"></i>'
26911 cls : 'btn-group roo-upload-cropbox-trash',
26916 cls : 'btn btn-default',
26917 html : '<i class="fa fa-trash"></i>'
26923 cls : 'btn-group roo-upload-cropbox-rotate-right',
26924 action : 'rotate-right',
26928 cls : 'btn btn-default',
26929 html : '<i class="fa fa-repeat"></i>'
26937 cls : 'btn-group roo-upload-cropbox-rotate-left',
26938 action : 'rotate-left',
26942 cls : 'btn btn-default',
26943 html : '<i class="fa fa-undo"></i>'
26949 cls : 'btn-group roo-upload-cropbox-rotate-right',
26950 action : 'rotate-right',
26954 cls : 'btn btn-default',
26955 html : '<i class="fa fa-repeat"></i>'
26968 * @class Roo.bootstrap.DocumentManager
26969 * @extends Roo.bootstrap.Component
26970 * Bootstrap DocumentManager class
26971 * @cfg {String} paramName default 'imageUpload'
26972 * @cfg {String} method default POST
26973 * @cfg {String} url action url
26974 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26975 * @cfg {Boolean} multiple multiple upload default true
26976 * @cfg {Number} thumbSize default 300
26977 * @cfg {String} fieldLabel
26978 * @cfg {Number} labelWidth default 4
26979 * @cfg {String} labelAlign (left|top) default left
26980 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26983 * Create a new DocumentManager
26984 * @param {Object} config The config object
26987 Roo.bootstrap.DocumentManager = function(config){
26988 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26993 * Fire when initial the DocumentManager
26994 * @param {Roo.bootstrap.DocumentManager} this
26999 * inspect selected file
27000 * @param {Roo.bootstrap.DocumentManager} this
27001 * @param {File} file
27006 * Fire when xhr load exception
27007 * @param {Roo.bootstrap.DocumentManager} this
27008 * @param {XMLHttpRequest} xhr
27010 "exception" : true,
27013 * prepare the form data
27014 * @param {Roo.bootstrap.DocumentManager} this
27015 * @param {Object} formData
27020 * Fire when remove the file
27021 * @param {Roo.bootstrap.DocumentManager} this
27022 * @param {Object} file
27027 * Fire after refresh the file
27028 * @param {Roo.bootstrap.DocumentManager} this
27033 * Fire after click the image
27034 * @param {Roo.bootstrap.DocumentManager} this
27035 * @param {Object} file
27040 * Fire when upload a image and editable set to true
27041 * @param {Roo.bootstrap.DocumentManager} this
27042 * @param {Object} file
27046 * @event beforeselectfile
27047 * Fire before select file
27048 * @param {Roo.bootstrap.DocumentManager} this
27050 "beforeselectfile" : true,
27053 * Fire before process file
27054 * @param {Roo.bootstrap.DocumentManager} this
27055 * @param {Object} file
27062 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27071 paramName : 'imageUpload',
27074 labelAlign : 'left',
27081 getAutoCreate : function()
27083 var managerWidget = {
27085 cls : 'roo-document-manager',
27089 cls : 'roo-document-manager-selector',
27094 cls : 'roo-document-manager-uploader',
27098 cls : 'roo-document-manager-upload-btn',
27099 html : '<i class="fa fa-plus"></i>'
27110 cls : 'column col-md-12',
27115 if(this.fieldLabel.length){
27120 cls : 'column col-md-12',
27121 html : this.fieldLabel
27125 cls : 'column col-md-12',
27130 if(this.labelAlign == 'left'){
27134 cls : 'column col-md-' + this.labelWidth,
27135 html : this.fieldLabel
27139 cls : 'column col-md-' + (12 - this.labelWidth),
27149 cls : 'row clearfix',
27157 initEvents : function()
27159 this.managerEl = this.el.select('.roo-document-manager', true).first();
27160 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27162 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27163 this.selectorEl.hide();
27166 this.selectorEl.attr('multiple', 'multiple');
27169 this.selectorEl.on('change', this.onFileSelected, this);
27171 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27172 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27174 this.uploader.on('click', this.onUploaderClick, this);
27176 this.renderProgressDialog();
27180 window.addEventListener("resize", function() { _this.refresh(); } );
27182 this.fireEvent('initial', this);
27185 renderProgressDialog : function()
27189 this.progressDialog = new Roo.bootstrap.Modal({
27190 cls : 'roo-document-manager-progress-dialog',
27191 allow_close : false,
27201 btnclick : function() {
27202 _this.uploadCancel();
27208 this.progressDialog.render(Roo.get(document.body));
27210 this.progress = new Roo.bootstrap.Progress({
27211 cls : 'roo-document-manager-progress',
27216 this.progress.render(this.progressDialog.getChildContainer());
27218 this.progressBar = new Roo.bootstrap.ProgressBar({
27219 cls : 'roo-document-manager-progress-bar',
27222 aria_valuemax : 12,
27226 this.progressBar.render(this.progress.getChildContainer());
27229 onUploaderClick : function(e)
27231 e.preventDefault();
27233 if(this.fireEvent('beforeselectfile', this) != false){
27234 this.selectorEl.dom.click();
27239 onFileSelected : function(e)
27241 e.preventDefault();
27243 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27247 Roo.each(this.selectorEl.dom.files, function(file){
27248 if(this.fireEvent('inspect', this, file) != false){
27249 this.files.push(file);
27259 this.selectorEl.dom.value = '';
27261 if(!this.files.length){
27265 if(this.boxes > 0 && this.files.length > this.boxes){
27266 this.files = this.files.slice(0, this.boxes);
27269 this.uploader.show();
27271 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27272 this.uploader.hide();
27281 Roo.each(this.files, function(file){
27283 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27284 var f = this.renderPreview(file);
27289 if(file.type.indexOf('image') != -1){
27290 this.delegates.push(
27292 _this.process(file);
27293 }).createDelegate(this)
27301 _this.process(file);
27302 }).createDelegate(this)
27307 this.files = files;
27309 this.delegates = this.delegates.concat(docs);
27311 if(!this.delegates.length){
27316 this.progressBar.aria_valuemax = this.delegates.length;
27323 arrange : function()
27325 if(!this.delegates.length){
27326 this.progressDialog.hide();
27331 var delegate = this.delegates.shift();
27333 this.progressDialog.show();
27335 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27337 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27342 refresh : function()
27344 this.uploader.show();
27346 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27347 this.uploader.hide();
27350 Roo.isTouch ? this.closable(false) : this.closable(true);
27352 this.fireEvent('refresh', this);
27355 onRemove : function(e, el, o)
27357 e.preventDefault();
27359 this.fireEvent('remove', this, o);
27363 remove : function(o)
27367 Roo.each(this.files, function(file){
27368 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27377 this.files = files;
27384 Roo.each(this.files, function(file){
27389 file.target.remove();
27398 onClick : function(e, el, o)
27400 e.preventDefault();
27402 this.fireEvent('click', this, o);
27406 closable : function(closable)
27408 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27410 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27422 xhrOnLoad : function(xhr)
27424 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27428 if (xhr.readyState !== 4) {
27430 this.fireEvent('exception', this, xhr);
27434 var response = Roo.decode(xhr.responseText);
27436 if(!response.success){
27438 this.fireEvent('exception', this, xhr);
27442 var file = this.renderPreview(response.data);
27444 this.files.push(file);
27450 xhrOnError : function(xhr)
27452 Roo.log('xhr on error');
27454 var response = Roo.decode(xhr.responseText);
27461 process : function(file)
27463 if(this.fireEvent('process', this, file) !== false){
27464 if(this.editable && file.type.indexOf('image') != -1){
27465 this.fireEvent('edit', this, file);
27469 this.uploadStart(file, false);
27476 uploadStart : function(file, crop)
27478 this.xhr = new XMLHttpRequest();
27480 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27485 file.xhr = this.xhr;
27487 this.managerEl.createChild({
27489 cls : 'roo-document-manager-loading',
27493 tooltip : file.name,
27494 cls : 'roo-document-manager-thumb',
27495 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27501 this.xhr.open(this.method, this.url, true);
27504 "Accept": "application/json",
27505 "Cache-Control": "no-cache",
27506 "X-Requested-With": "XMLHttpRequest"
27509 for (var headerName in headers) {
27510 var headerValue = headers[headerName];
27512 this.xhr.setRequestHeader(headerName, headerValue);
27518 this.xhr.onload = function()
27520 _this.xhrOnLoad(_this.xhr);
27523 this.xhr.onerror = function()
27525 _this.xhrOnError(_this.xhr);
27528 var formData = new FormData();
27530 formData.append('returnHTML', 'NO');
27533 formData.append('crop', crop);
27536 formData.append(this.paramName, file, file.name);
27538 if(this.fireEvent('prepare', this, formData) != false){
27539 this.xhr.send(formData);
27543 uploadCancel : function()
27550 this.delegates = [];
27552 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27559 renderPreview : function(file)
27561 if(typeof(file.target) != 'undefined' && file.target){
27565 var previewEl = this.managerEl.createChild({
27567 cls : 'roo-document-manager-preview',
27571 tooltip : file.filename,
27572 cls : 'roo-document-manager-thumb',
27573 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27578 html : '<i class="fa fa-times-circle"></i>'
27583 var close = previewEl.select('button.close', true).first();
27585 close.on('click', this.onRemove, this, file);
27587 file.target = previewEl;
27589 var image = previewEl.select('img', true).first();
27593 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27595 image.on('click', this.onClick, this, file);
27601 onPreviewLoad : function(file, image)
27603 if(typeof(file.target) == 'undefined' || !file.target){
27607 var width = image.dom.naturalWidth || image.dom.width;
27608 var height = image.dom.naturalHeight || image.dom.height;
27610 if(width > height){
27611 file.target.addClass('wide');
27615 file.target.addClass('tall');
27620 uploadFromSource : function(file, crop)
27622 this.xhr = new XMLHttpRequest();
27624 this.managerEl.createChild({
27626 cls : 'roo-document-manager-loading',
27630 tooltip : file.name,
27631 cls : 'roo-document-manager-thumb',
27632 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27638 this.xhr.open(this.method, this.url, true);
27641 "Accept": "application/json",
27642 "Cache-Control": "no-cache",
27643 "X-Requested-With": "XMLHttpRequest"
27646 for (var headerName in headers) {
27647 var headerValue = headers[headerName];
27649 this.xhr.setRequestHeader(headerName, headerValue);
27655 this.xhr.onload = function()
27657 _this.xhrOnLoad(_this.xhr);
27660 this.xhr.onerror = function()
27662 _this.xhrOnError(_this.xhr);
27665 var formData = new FormData();
27667 formData.append('returnHTML', 'NO');
27669 formData.append('crop', crop);
27671 if(typeof(file.filename) != 'undefined'){
27672 formData.append('filename', file.filename);
27675 if(typeof(file.mimetype) != 'undefined'){
27676 formData.append('mimetype', file.mimetype);
27679 if(this.fireEvent('prepare', this, formData) != false){
27680 this.xhr.send(formData);
27690 * @class Roo.bootstrap.DocumentViewer
27691 * @extends Roo.bootstrap.Component
27692 * Bootstrap DocumentViewer class
27695 * Create a new DocumentViewer
27696 * @param {Object} config The config object
27699 Roo.bootstrap.DocumentViewer = function(config){
27700 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27705 * Fire after initEvent
27706 * @param {Roo.bootstrap.DocumentViewer} this
27712 * @param {Roo.bootstrap.DocumentViewer} this
27717 * Fire after trash button
27718 * @param {Roo.bootstrap.DocumentViewer} this
27725 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27727 getAutoCreate : function()
27731 cls : 'roo-document-viewer',
27735 cls : 'roo-document-viewer-body',
27739 cls : 'roo-document-viewer-thumb',
27743 cls : 'roo-document-viewer-image'
27751 cls : 'roo-document-viewer-footer',
27754 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27762 cls : 'btn btn-default roo-document-viewer-trash',
27763 html : '<i class="fa fa-trash"></i>'
27776 initEvents : function()
27779 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27780 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27782 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27783 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27785 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27786 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27788 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27789 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27791 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27792 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27794 this.bodyEl.on('click', this.onClick, this);
27796 this.trashBtn.on('click', this.onTrash, this);
27800 initial : function()
27802 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27805 this.fireEvent('initial', this);
27809 onClick : function(e)
27811 e.preventDefault();
27813 this.fireEvent('click', this);
27816 onTrash : function(e)
27818 e.preventDefault();
27820 this.fireEvent('trash', this);
27832 * @class Roo.bootstrap.NavProgressBar
27833 * @extends Roo.bootstrap.Component
27834 * Bootstrap NavProgressBar class
27837 * Create a new nav progress bar
27838 * @param {Object} config The config object
27841 Roo.bootstrap.NavProgressBar = function(config){
27842 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27844 this.bullets = this.bullets || [];
27846 // Roo.bootstrap.NavProgressBar.register(this);
27850 * Fires when the active item changes
27851 * @param {Roo.bootstrap.NavProgressBar} this
27852 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27853 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27860 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27865 getAutoCreate : function()
27867 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27871 cls : 'roo-navigation-bar-group',
27875 cls : 'roo-navigation-top-bar'
27879 cls : 'roo-navigation-bullets-bar',
27883 cls : 'roo-navigation-bar'
27890 cls : 'roo-navigation-bottom-bar'
27900 initEvents: function()
27905 onRender : function(ct, position)
27907 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27909 if(this.bullets.length){
27910 Roo.each(this.bullets, function(b){
27919 addItem : function(cfg)
27921 var item = new Roo.bootstrap.NavProgressItem(cfg);
27923 item.parentId = this.id;
27924 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27927 var top = new Roo.bootstrap.Element({
27929 cls : 'roo-navigation-bar-text'
27932 var bottom = new Roo.bootstrap.Element({
27934 cls : 'roo-navigation-bar-text'
27937 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27938 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27940 var topText = new Roo.bootstrap.Element({
27942 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27945 var bottomText = new Roo.bootstrap.Element({
27947 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27950 topText.onRender(top.el, null);
27951 bottomText.onRender(bottom.el, null);
27954 item.bottomEl = bottom;
27957 this.barItems.push(item);
27962 getActive : function()
27964 var active = false;
27966 Roo.each(this.barItems, function(v){
27968 if (!v.isActive()) {
27980 setActiveItem : function(item)
27984 Roo.each(this.barItems, function(v){
27985 if (v.rid == item.rid) {
27989 if (v.isActive()) {
27990 v.setActive(false);
27995 item.setActive(true);
27997 this.fireEvent('changed', this, item, prev);
28000 getBarItem: function(rid)
28004 Roo.each(this.barItems, function(e) {
28005 if (e.rid != rid) {
28016 indexOfItem : function(item)
28020 Roo.each(this.barItems, function(v, i){
28022 if (v.rid != item.rid) {
28033 setActiveNext : function()
28035 var i = this.indexOfItem(this.getActive());
28037 if (i > this.barItems.length) {
28041 this.setActiveItem(this.barItems[i+1]);
28044 setActivePrev : function()
28046 var i = this.indexOfItem(this.getActive());
28052 this.setActiveItem(this.barItems[i-1]);
28055 format : function()
28057 if(!this.barItems.length){
28061 var width = 100 / this.barItems.length;
28063 Roo.each(this.barItems, function(i){
28064 i.el.setStyle('width', width + '%');
28065 i.topEl.el.setStyle('width', width + '%');
28066 i.bottomEl.el.setStyle('width', width + '%');
28075 * Nav Progress Item
28080 * @class Roo.bootstrap.NavProgressItem
28081 * @extends Roo.bootstrap.Component
28082 * Bootstrap NavProgressItem class
28083 * @cfg {String} rid the reference id
28084 * @cfg {Boolean} active (true|false) Is item active default false
28085 * @cfg {Boolean} disabled (true|false) Is item active default false
28086 * @cfg {String} html
28087 * @cfg {String} position (top|bottom) text position default bottom
28088 * @cfg {String} icon show icon instead of number
28091 * Create a new NavProgressItem
28092 * @param {Object} config The config object
28094 Roo.bootstrap.NavProgressItem = function(config){
28095 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28100 * The raw click event for the entire grid.
28101 * @param {Roo.bootstrap.NavProgressItem} this
28102 * @param {Roo.EventObject} e
28109 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28115 position : 'bottom',
28118 getAutoCreate : function()
28120 var iconCls = 'roo-navigation-bar-item-icon';
28122 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28126 cls: 'roo-navigation-bar-item',
28136 cfg.cls += ' active';
28139 cfg.cls += ' disabled';
28145 disable : function()
28147 this.setDisabled(true);
28150 enable : function()
28152 this.setDisabled(false);
28155 initEvents: function()
28157 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28159 this.iconEl.on('click', this.onClick, this);
28162 onClick : function(e)
28164 e.preventDefault();
28170 if(this.fireEvent('click', this, e) === false){
28174 this.parent().setActiveItem(this);
28177 isActive: function ()
28179 return this.active;
28182 setActive : function(state)
28184 if(this.active == state){
28188 this.active = state;
28191 this.el.addClass('active');
28195 this.el.removeClass('active');
28200 setDisabled : function(state)
28202 if(this.disabled == state){
28206 this.disabled = state;
28209 this.el.addClass('disabled');
28213 this.el.removeClass('disabled');
28216 tooltipEl : function()
28218 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28231 * @class Roo.bootstrap.FieldLabel
28232 * @extends Roo.bootstrap.Component
28233 * Bootstrap FieldLabel class
28234 * @cfg {String} html contents of the element
28235 * @cfg {String} tag tag of the element default label
28236 * @cfg {String} cls class of the element
28237 * @cfg {String} target label target
28238 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28239 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28240 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28241 * @cfg {String} iconTooltip default "This field is required"
28244 * Create a new FieldLabel
28245 * @param {Object} config The config object
28248 Roo.bootstrap.FieldLabel = function(config){
28249 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28254 * Fires after the field has been marked as invalid.
28255 * @param {Roo.form.FieldLabel} this
28256 * @param {String} msg The validation message
28261 * Fires after the field has been validated with no errors.
28262 * @param {Roo.form.FieldLabel} this
28268 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28275 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28276 validClass : 'text-success fa fa-lg fa-check',
28277 iconTooltip : 'This field is required',
28279 getAutoCreate : function(){
28283 cls : 'roo-bootstrap-field-label ' + this.cls,
28289 tooltip : this.iconTooltip
28301 initEvents: function()
28303 Roo.bootstrap.Element.superclass.initEvents.call(this);
28305 this.iconEl = this.el.select('i', true).first();
28307 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28309 Roo.bootstrap.FieldLabel.register(this);
28313 * Mark this field as valid
28315 markValid : function()
28317 this.iconEl.show();
28319 this.iconEl.removeClass(this.invalidClass);
28321 this.iconEl.addClass(this.validClass);
28323 this.fireEvent('valid', this);
28327 * Mark this field as invalid
28328 * @param {String} msg The validation message
28330 markInvalid : function(msg)
28332 this.iconEl.show();
28334 this.iconEl.removeClass(this.validClass);
28336 this.iconEl.addClass(this.invalidClass);
28338 this.fireEvent('invalid', this, msg);
28344 Roo.apply(Roo.bootstrap.FieldLabel, {
28349 * register a FieldLabel Group
28350 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28352 register : function(label)
28354 if(this.groups.hasOwnProperty(label.target)){
28358 this.groups[label.target] = label;
28362 * fetch a FieldLabel Group based on the target
28363 * @param {string} target
28364 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28366 get: function(target) {
28367 if (typeof(this.groups[target]) == 'undefined') {
28371 return this.groups[target] ;
28380 * page DateSplitField.
28386 * @class Roo.bootstrap.DateSplitField
28387 * @extends Roo.bootstrap.Component
28388 * Bootstrap DateSplitField class
28389 * @cfg {string} fieldLabel - the label associated
28390 * @cfg {Number} labelWidth set the width of label (0-12)
28391 * @cfg {String} labelAlign (top|left)
28392 * @cfg {Boolean} dayAllowBlank (true|false) default false
28393 * @cfg {Boolean} monthAllowBlank (true|false) default false
28394 * @cfg {Boolean} yearAllowBlank (true|false) default false
28395 * @cfg {string} dayPlaceholder
28396 * @cfg {string} monthPlaceholder
28397 * @cfg {string} yearPlaceholder
28398 * @cfg {string} dayFormat default 'd'
28399 * @cfg {string} monthFormat default 'm'
28400 * @cfg {string} yearFormat default 'Y'
28404 * Create a new DateSplitField
28405 * @param {Object} config The config object
28408 Roo.bootstrap.DateSplitField = function(config){
28409 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28415 * getting the data of years
28416 * @param {Roo.bootstrap.DateSplitField} this
28417 * @param {Object} years
28422 * getting the data of days
28423 * @param {Roo.bootstrap.DateSplitField} this
28424 * @param {Object} days
28429 * Fires after the field has been marked as invalid.
28430 * @param {Roo.form.Field} this
28431 * @param {String} msg The validation message
28436 * Fires after the field has been validated with no errors.
28437 * @param {Roo.form.Field} this
28443 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28446 labelAlign : 'top',
28448 dayAllowBlank : false,
28449 monthAllowBlank : false,
28450 yearAllowBlank : false,
28451 dayPlaceholder : '',
28452 monthPlaceholder : '',
28453 yearPlaceholder : '',
28457 isFormField : true,
28459 getAutoCreate : function()
28463 cls : 'row roo-date-split-field-group',
28468 cls : 'form-hidden-field roo-date-split-field-group-value',
28474 if(this.fieldLabel){
28477 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28481 html : this.fieldLabel
28487 Roo.each(['day', 'month', 'year'], function(t){
28490 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28497 inputEl: function ()
28499 return this.el.select('.roo-date-split-field-group-value', true).first();
28502 onRender : function(ct, position)
28506 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28508 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28510 this.dayField = new Roo.bootstrap.ComboBox({
28511 allowBlank : this.dayAllowBlank,
28512 alwaysQuery : true,
28513 displayField : 'value',
28516 forceSelection : true,
28518 placeholder : this.dayPlaceholder,
28519 selectOnFocus : true,
28520 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28521 triggerAction : 'all',
28523 valueField : 'value',
28524 store : new Roo.data.SimpleStore({
28525 data : (function() {
28527 _this.fireEvent('days', _this, days);
28530 fields : [ 'value' ]
28533 select : function (_self, record, index)
28535 _this.setValue(_this.getValue());
28540 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28542 this.monthField = new Roo.bootstrap.MonthField({
28543 after : '<i class=\"fa fa-calendar\"></i>',
28544 allowBlank : this.monthAllowBlank,
28545 placeholder : this.monthPlaceholder,
28548 render : function (_self)
28550 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28551 e.preventDefault();
28555 select : function (_self, oldvalue, newvalue)
28557 _this.setValue(_this.getValue());
28562 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28564 this.yearField = new Roo.bootstrap.ComboBox({
28565 allowBlank : this.yearAllowBlank,
28566 alwaysQuery : true,
28567 displayField : 'value',
28570 forceSelection : true,
28572 placeholder : this.yearPlaceholder,
28573 selectOnFocus : true,
28574 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28575 triggerAction : 'all',
28577 valueField : 'value',
28578 store : new Roo.data.SimpleStore({
28579 data : (function() {
28581 _this.fireEvent('years', _this, years);
28584 fields : [ 'value' ]
28587 select : function (_self, record, index)
28589 _this.setValue(_this.getValue());
28594 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28597 setValue : function(v, format)
28599 this.inputEl.dom.value = v;
28601 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28603 var d = Date.parseDate(v, f);
28610 this.setDay(d.format(this.dayFormat));
28611 this.setMonth(d.format(this.monthFormat));
28612 this.setYear(d.format(this.yearFormat));
28619 setDay : function(v)
28621 this.dayField.setValue(v);
28622 this.inputEl.dom.value = this.getValue();
28627 setMonth : function(v)
28629 this.monthField.setValue(v, true);
28630 this.inputEl.dom.value = this.getValue();
28635 setYear : function(v)
28637 this.yearField.setValue(v);
28638 this.inputEl.dom.value = this.getValue();
28643 getDay : function()
28645 return this.dayField.getValue();
28648 getMonth : function()
28650 return this.monthField.getValue();
28653 getYear : function()
28655 return this.yearField.getValue();
28658 getValue : function()
28660 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28662 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28672 this.inputEl.dom.value = '';
28677 validate : function()
28679 var d = this.dayField.validate();
28680 var m = this.monthField.validate();
28681 var y = this.yearField.validate();
28686 (!this.dayAllowBlank && !d) ||
28687 (!this.monthAllowBlank && !m) ||
28688 (!this.yearAllowBlank && !y)
28693 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28702 this.markInvalid();
28707 markValid : function()
28710 var label = this.el.select('label', true).first();
28711 var icon = this.el.select('i.fa-star', true).first();
28717 this.fireEvent('valid', this);
28721 * Mark this field as invalid
28722 * @param {String} msg The validation message
28724 markInvalid : function(msg)
28727 var label = this.el.select('label', true).first();
28728 var icon = this.el.select('i.fa-star', true).first();
28730 if(label && !icon){
28731 this.el.select('.roo-date-split-field-label', true).createChild({
28733 cls : 'text-danger fa fa-lg fa-star',
28734 tooltip : 'This field is required',
28735 style : 'margin-right:5px;'
28739 this.fireEvent('invalid', this, msg);
28742 clearInvalid : function()
28744 var label = this.el.select('label', true).first();
28745 var icon = this.el.select('i.fa-star', true).first();
28751 this.fireEvent('valid', this);
28754 getName: function()
28764 * http://masonry.desandro.com
28766 * The idea is to render all the bricks based on vertical width...
28768 * The original code extends 'outlayer' - we might need to use that....
28774 * @class Roo.bootstrap.LayoutMasonry
28775 * @extends Roo.bootstrap.Component
28776 * Bootstrap Layout Masonry class
28779 * Create a new Element
28780 * @param {Object} config The config object
28783 Roo.bootstrap.LayoutMasonry = function(config){
28784 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28790 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
28793 * @cfg {Boolean} isLayoutInstant = no animation?
28795 isLayoutInstant : false, // needed?
28798 * @cfg {Number} boxWidth width of the columns
28803 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
28808 * @cfg {Number} padWidth padding below box..
28813 * @cfg {Number} gutter gutter width..
28818 * @cfg {Number} maxCols maximum number of columns
28824 * @cfg {Boolean} isAutoInitial defalut true
28826 isAutoInitial : true,
28831 * @cfg {Boolean} isHorizontal defalut false
28833 isHorizontal : false,
28835 currentSize : null,
28841 bricks: null, //CompositeElement
28845 _isLayoutInited : false,
28847 // isAlternative : false, // only use for vertical layout...
28850 * @cfg {Number} alternativePadWidth padding below box..
28852 alternativePadWidth : 50,
28854 getAutoCreate : function(){
28858 cls: 'blog-masonary-wrapper ' + this.cls,
28860 cls : 'mas-boxes masonary'
28867 getChildContainer: function( )
28869 if (this.boxesEl) {
28870 return this.boxesEl;
28873 this.boxesEl = this.el.select('.mas-boxes').first();
28875 return this.boxesEl;
28879 initEvents : function()
28883 if(this.isAutoInitial){
28884 Roo.log('hook children rendered');
28885 this.on('childrenrendered', function() {
28886 Roo.log('children rendered');
28892 initial : function()
28894 this.currentSize = this.el.getBox(true);
28896 Roo.EventManager.onWindowResize(this.resize, this);
28898 if(!this.isAutoInitial){
28906 //this.layout.defer(500,this);
28910 resize : function()
28914 var cs = this.el.getBox(true);
28916 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28917 Roo.log("no change in with or X");
28921 this.currentSize = cs;
28927 layout : function()
28929 this._resetLayout();
28931 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28933 this.layoutItems( isInstant );
28935 this._isLayoutInited = true;
28939 _resetLayout : function()
28941 if(this.isHorizontal){
28942 this.horizontalMeasureColumns();
28946 this.verticalMeasureColumns();
28950 verticalMeasureColumns : function()
28952 this.getContainerWidth();
28954 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28955 // this.colWidth = Math.floor(this.containerWidth * 0.8);
28959 var boxWidth = this.boxWidth + this.padWidth;
28961 if(this.containerWidth < this.boxWidth){
28962 boxWidth = this.containerWidth
28965 var containerWidth = this.containerWidth;
28967 var cols = Math.floor(containerWidth / boxWidth);
28969 this.cols = Math.max( cols, 1 );
28971 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28973 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28975 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28977 this.colWidth = boxWidth + avail - this.padWidth;
28979 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28980 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
28983 horizontalMeasureColumns : function()
28985 this.getContainerWidth();
28987 var boxWidth = this.boxWidth;
28989 if(this.containerWidth < boxWidth){
28990 boxWidth = this.containerWidth;
28993 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28995 this.el.setHeight(boxWidth);
28999 getContainerWidth : function()
29001 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29004 layoutItems : function( isInstant )
29006 var items = Roo.apply([], this.bricks);
29008 if(this.isHorizontal){
29009 this._horizontalLayoutItems( items , isInstant );
29013 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29014 // this._verticalAlternativeLayoutItems( items , isInstant );
29018 this._verticalLayoutItems( items , isInstant );
29022 _verticalLayoutItems : function ( items , isInstant)
29024 if ( !items || !items.length ) {
29029 ['xs', 'xs', 'xs', 'tall'],
29030 ['xs', 'xs', 'tall'],
29031 ['xs', 'xs', 'sm'],
29032 ['xs', 'xs', 'xs'],
29038 ['sm', 'xs', 'xs'],
29042 ['tall', 'xs', 'xs', 'xs'],
29043 ['tall', 'xs', 'xs'],
29055 Roo.each(items, function(item, k){
29057 switch (item.size) {
29058 // these layouts take up a full box,
29069 boxes.push([item]);
29092 var filterPattern = function(box, length)
29100 var pattern = box.slice(0, length);
29104 Roo.each(pattern, function(i){
29105 format.push(i.size);
29108 Roo.each(standard, function(s){
29110 if(String(s) != String(format)){
29119 if(!match && length == 1){
29124 filterPattern(box, length - 1);
29128 queue.push(pattern);
29130 box = box.slice(length, box.length);
29132 filterPattern(box, 4);
29138 Roo.each(boxes, function(box, k){
29144 if(box.length == 1){
29149 filterPattern(box, 4);
29153 this._processVerticalLayoutQueue( queue, isInstant );
29157 // _verticalAlternativeLayoutItems : function( items , isInstant )
29159 // if ( !items || !items.length ) {
29163 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29167 _horizontalLayoutItems : function ( items , isInstant)
29169 if ( !items || !items.length || items.length < 3) {
29175 var eItems = items.slice(0, 3);
29177 items = items.slice(3, items.length);
29180 ['xs', 'xs', 'xs', 'wide'],
29181 ['xs', 'xs', 'wide'],
29182 ['xs', 'xs', 'sm'],
29183 ['xs', 'xs', 'xs'],
29189 ['sm', 'xs', 'xs'],
29193 ['wide', 'xs', 'xs', 'xs'],
29194 ['wide', 'xs', 'xs'],
29207 Roo.each(items, function(item, k){
29209 switch (item.size) {
29220 boxes.push([item]);
29244 var filterPattern = function(box, length)
29252 var pattern = box.slice(0, length);
29256 Roo.each(pattern, function(i){
29257 format.push(i.size);
29260 Roo.each(standard, function(s){
29262 if(String(s) != String(format)){
29271 if(!match && length == 1){
29276 filterPattern(box, length - 1);
29280 queue.push(pattern);
29282 box = box.slice(length, box.length);
29284 filterPattern(box, 4);
29290 Roo.each(boxes, function(box, k){
29296 if(box.length == 1){
29301 filterPattern(box, 4);
29308 var pos = this.el.getBox(true);
29312 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29314 var hit_end = false;
29316 Roo.each(queue, function(box){
29320 Roo.each(box, function(b){
29322 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29332 Roo.each(box, function(b){
29334 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29337 mx = Math.max(mx, b.x);
29341 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29345 Roo.each(box, function(b){
29347 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29361 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29364 /** Sets position of item in DOM
29365 * @param {Element} item
29366 * @param {Number} x - horizontal position
29367 * @param {Number} y - vertical position
29368 * @param {Boolean} isInstant - disables transitions
29370 _processVerticalLayoutQueue : function( queue, isInstant )
29372 var pos = this.el.getBox(true);
29377 for (var i = 0; i < this.cols; i++){
29381 Roo.each(queue, function(box, k){
29383 var col = k % this.cols;
29385 Roo.each(box, function(b,kk){
29387 b.el.position('absolute');
29389 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29390 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29392 if(b.size == 'md-left' || b.size == 'md-right'){
29393 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29394 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29397 b.el.setWidth(width);
29398 b.el.setHeight(height);
29400 b.el.select('iframe',true).setSize(width,height);
29404 for (var i = 0; i < this.cols; i++){
29406 if(maxY[i] < maxY[col]){
29411 col = Math.min(col, i);
29415 x = pos.x + col * (this.colWidth + this.padWidth);
29419 var positions = [];
29421 switch (box.length){
29423 positions = this.getVerticalOneBoxColPositions(x, y, box);
29426 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29429 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29432 positions = this.getVerticalFourBoxColPositions(x, y, box);
29438 Roo.each(box, function(b,kk){
29440 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29442 var sz = b.el.getSize();
29444 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29452 for (var i = 0; i < this.cols; i++){
29453 mY = Math.max(mY, maxY[i]);
29456 this.el.setHeight(mY - pos.y);
29460 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29462 // var pos = this.el.getBox(true);
29465 // var maxX = pos.right;
29467 // var maxHeight = 0;
29469 // Roo.each(items, function(item, k){
29473 // item.el.position('absolute');
29475 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29477 // item.el.setWidth(width);
29479 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29481 // item.el.setHeight(height);
29484 // item.el.setXY([x, y], isInstant ? false : true);
29486 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29489 // y = y + height + this.alternativePadWidth;
29491 // maxHeight = maxHeight + height + this.alternativePadWidth;
29495 // this.el.setHeight(maxHeight);
29499 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29501 var pos = this.el.getBox(true);
29506 var maxX = pos.right;
29508 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29510 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29512 Roo.each(queue, function(box, k){
29514 Roo.each(box, function(b, kk){
29516 b.el.position('absolute');
29518 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29519 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29521 if(b.size == 'md-left' || b.size == 'md-right'){
29522 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29523 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29526 b.el.setWidth(width);
29527 b.el.setHeight(height);
29535 var positions = [];
29537 switch (box.length){
29539 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29542 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29545 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29548 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29554 Roo.each(box, function(b,kk){
29556 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29558 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29566 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29568 Roo.each(eItems, function(b,k){
29570 b.size = (k == 0) ? 'sm' : 'xs';
29571 b.x = (k == 0) ? 2 : 1;
29572 b.y = (k == 0) ? 2 : 1;
29574 b.el.position('absolute');
29576 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29578 b.el.setWidth(width);
29580 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29582 b.el.setHeight(height);
29586 var positions = [];
29589 x : maxX - this.unitWidth * 2 - this.gutter,
29594 x : maxX - this.unitWidth,
29595 y : minY + (this.unitWidth + this.gutter) * 2
29599 x : maxX - this.unitWidth * 3 - this.gutter * 2,
29603 Roo.each(eItems, function(b,k){
29605 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29611 getVerticalOneBoxColPositions : function(x, y, box)
29615 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29617 if(box[0].size == 'md-left'){
29621 if(box[0].size == 'md-right'){
29626 x : x + (this.unitWidth + this.gutter) * rand,
29633 getVerticalTwoBoxColPositions : function(x, y, box)
29637 if(box[0].size == 'xs'){
29641 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29645 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29659 x : x + (this.unitWidth + this.gutter) * 2,
29660 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29667 getVerticalThreeBoxColPositions : function(x, y, box)
29671 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29679 x : x + (this.unitWidth + this.gutter) * 1,
29684 x : x + (this.unitWidth + this.gutter) * 2,
29692 if(box[0].size == 'xs' && box[1].size == 'xs'){
29701 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29705 x : x + (this.unitWidth + this.gutter) * 1,
29719 x : x + (this.unitWidth + this.gutter) * 2,
29724 x : x + (this.unitWidth + this.gutter) * 2,
29725 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29732 getVerticalFourBoxColPositions : function(x, y, box)
29736 if(box[0].size == 'xs'){
29745 y : y + (this.unitHeight + this.gutter) * 1
29750 y : y + (this.unitHeight + this.gutter) * 2
29754 x : x + (this.unitWidth + this.gutter) * 1,
29768 x : x + (this.unitWidth + this.gutter) * 2,
29773 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29774 y : y + (this.unitHeight + this.gutter) * 1
29778 x : x + (this.unitWidth + this.gutter) * 2,
29779 y : y + (this.unitWidth + this.gutter) * 2
29786 getHorizontalOneBoxColPositions : function(maxX, minY, box)
29790 if(box[0].size == 'md-left'){
29792 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29799 if(box[0].size == 'md-right'){
29801 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29802 y : minY + (this.unitWidth + this.gutter) * 1
29808 var rand = Math.floor(Math.random() * (4 - box[0].y));
29811 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29812 y : minY + (this.unitWidth + this.gutter) * rand
29819 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29823 if(box[0].size == 'xs'){
29826 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29831 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29832 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29840 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29845 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29846 y : minY + (this.unitWidth + this.gutter) * 2
29853 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29857 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29860 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29865 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29866 y : minY + (this.unitWidth + this.gutter) * 1
29870 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29871 y : minY + (this.unitWidth + this.gutter) * 2
29878 if(box[0].size == 'xs' && box[1].size == 'xs'){
29881 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29886 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29891 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29892 y : minY + (this.unitWidth + this.gutter) * 1
29900 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29905 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29906 y : minY + (this.unitWidth + this.gutter) * 2
29910 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29911 y : minY + (this.unitWidth + this.gutter) * 2
29918 getHorizontalFourBoxColPositions : function(maxX, minY, box)
29922 if(box[0].size == 'xs'){
29925 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29930 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29935 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),
29940 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29941 y : minY + (this.unitWidth + this.gutter) * 1
29949 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29954 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29955 y : minY + (this.unitWidth + this.gutter) * 2
29959 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29960 y : minY + (this.unitWidth + this.gutter) * 2
29964 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),
29965 y : minY + (this.unitWidth + this.gutter) * 2
29979 * http://masonry.desandro.com
29981 * The idea is to render all the bricks based on vertical width...
29983 * The original code extends 'outlayer' - we might need to use that....
29989 * @class Roo.bootstrap.LayoutMasonryAuto
29990 * @extends Roo.bootstrap.Component
29991 * Bootstrap Layout Masonry class
29994 * Create a new Element
29995 * @param {Object} config The config object
29998 Roo.bootstrap.LayoutMasonryAuto = function(config){
29999 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30002 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30005 * @cfg {Boolean} isFitWidth - resize the width..
30007 isFitWidth : false, // options..
30009 * @cfg {Boolean} isOriginLeft = left align?
30011 isOriginLeft : true,
30013 * @cfg {Boolean} isOriginTop = top align?
30015 isOriginTop : false,
30017 * @cfg {Boolean} isLayoutInstant = no animation?
30019 isLayoutInstant : false, // needed?
30021 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30023 isResizingContainer : true,
30025 * @cfg {Number} columnWidth width of the columns
30031 * @cfg {Number} maxCols maximum number of columns
30036 * @cfg {Number} padHeight padding below box..
30042 * @cfg {Boolean} isAutoInitial defalut true
30045 isAutoInitial : true,
30051 initialColumnWidth : 0,
30052 currentSize : null,
30054 colYs : null, // array.
30061 bricks: null, //CompositeElement
30062 cols : 0, // array?
30063 // element : null, // wrapped now this.el
30064 _isLayoutInited : null,
30067 getAutoCreate : function(){
30071 cls: 'blog-masonary-wrapper ' + this.cls,
30073 cls : 'mas-boxes masonary'
30080 getChildContainer: function( )
30082 if (this.boxesEl) {
30083 return this.boxesEl;
30086 this.boxesEl = this.el.select('.mas-boxes').first();
30088 return this.boxesEl;
30092 initEvents : function()
30096 if(this.isAutoInitial){
30097 Roo.log('hook children rendered');
30098 this.on('childrenrendered', function() {
30099 Roo.log('children rendered');
30106 initial : function()
30108 this.reloadItems();
30110 this.currentSize = this.el.getBox(true);
30112 /// was window resize... - let's see if this works..
30113 Roo.EventManager.onWindowResize(this.resize, this);
30115 if(!this.isAutoInitial){
30120 this.layout.defer(500,this);
30123 reloadItems: function()
30125 this.bricks = this.el.select('.masonry-brick', true);
30127 this.bricks.each(function(b) {
30128 //Roo.log(b.getSize());
30129 if (!b.attr('originalwidth')) {
30130 b.attr('originalwidth', b.getSize().width);
30135 Roo.log(this.bricks.elements.length);
30138 resize : function()
30141 var cs = this.el.getBox(true);
30143 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30144 Roo.log("no change in with or X");
30147 this.currentSize = cs;
30151 layout : function()
30154 this._resetLayout();
30155 //this._manageStamps();
30157 // don't animate first layout
30158 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30159 this.layoutItems( isInstant );
30161 // flag for initalized
30162 this._isLayoutInited = true;
30165 layoutItems : function( isInstant )
30167 //var items = this._getItemsForLayout( this.items );
30168 // original code supports filtering layout items.. we just ignore it..
30170 this._layoutItems( this.bricks , isInstant );
30172 this._postLayout();
30174 _layoutItems : function ( items , isInstant)
30176 //this.fireEvent( 'layout', this, items );
30179 if ( !items || !items.elements.length ) {
30180 // no items, emit event with empty array
30185 items.each(function(item) {
30186 Roo.log("layout item");
30188 // get x/y object from method
30189 var position = this._getItemLayoutPosition( item );
30191 position.item = item;
30192 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30193 queue.push( position );
30196 this._processLayoutQueue( queue );
30198 /** Sets position of item in DOM
30199 * @param {Element} item
30200 * @param {Number} x - horizontal position
30201 * @param {Number} y - vertical position
30202 * @param {Boolean} isInstant - disables transitions
30204 _processLayoutQueue : function( queue )
30206 for ( var i=0, len = queue.length; i < len; i++ ) {
30207 var obj = queue[i];
30208 obj.item.position('absolute');
30209 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30215 * Any logic you want to do after each layout,
30216 * i.e. size the container
30218 _postLayout : function()
30220 this.resizeContainer();
30223 resizeContainer : function()
30225 if ( !this.isResizingContainer ) {
30228 var size = this._getContainerSize();
30230 this.el.setSize(size.width,size.height);
30231 this.boxesEl.setSize(size.width,size.height);
30237 _resetLayout : function()
30239 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30240 this.colWidth = this.el.getWidth();
30241 //this.gutter = this.el.getWidth();
30243 this.measureColumns();
30249 this.colYs.push( 0 );
30255 measureColumns : function()
30257 this.getContainerWidth();
30258 // if columnWidth is 0, default to outerWidth of first item
30259 if ( !this.columnWidth ) {
30260 var firstItem = this.bricks.first();
30261 Roo.log(firstItem);
30262 this.columnWidth = this.containerWidth;
30263 if (firstItem && firstItem.attr('originalwidth') ) {
30264 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30266 // columnWidth fall back to item of first element
30267 Roo.log("set column width?");
30268 this.initialColumnWidth = this.columnWidth ;
30270 // if first elem has no width, default to size of container
30275 if (this.initialColumnWidth) {
30276 this.columnWidth = this.initialColumnWidth;
30281 // column width is fixed at the top - however if container width get's smaller we should
30284 // this bit calcs how man columns..
30286 var columnWidth = this.columnWidth += this.gutter;
30288 // calculate columns
30289 var containerWidth = this.containerWidth + this.gutter;
30291 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30292 // fix rounding errors, typically with gutters
30293 var excess = columnWidth - containerWidth % columnWidth;
30296 // if overshoot is less than a pixel, round up, otherwise floor it
30297 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30298 cols = Math[ mathMethod ]( cols );
30299 this.cols = Math.max( cols, 1 );
30300 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30302 // padding positioning..
30303 var totalColWidth = this.cols * this.columnWidth;
30304 var padavail = this.containerWidth - totalColWidth;
30305 // so for 2 columns - we need 3 'pads'
30307 var padNeeded = (1+this.cols) * this.padWidth;
30309 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30311 this.columnWidth += padExtra
30312 //this.padWidth = Math.floor(padavail / ( this.cols));
30314 // adjust colum width so that padding is fixed??
30316 // we have 3 columns ... total = width * 3
30317 // we have X left over... that should be used by
30319 //if (this.expandC) {
30327 getContainerWidth : function()
30329 /* // container is parent if fit width
30330 var container = this.isFitWidth ? this.element.parentNode : this.element;
30331 // check that this.size and size are there
30332 // IE8 triggers resize on body size change, so they might not be
30334 var size = getSize( container ); //FIXME
30335 this.containerWidth = size && size.innerWidth; //FIXME
30338 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30342 _getItemLayoutPosition : function( item ) // what is item?
30344 // we resize the item to our columnWidth..
30346 item.setWidth(this.columnWidth);
30347 item.autoBoxAdjust = false;
30349 var sz = item.getSize();
30351 // how many columns does this brick span
30352 var remainder = this.containerWidth % this.columnWidth;
30354 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30355 // round if off by 1 pixel, otherwise use ceil
30356 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30357 colSpan = Math.min( colSpan, this.cols );
30359 // normally this should be '1' as we dont' currently allow multi width columns..
30361 var colGroup = this._getColGroup( colSpan );
30362 // get the minimum Y value from the columns
30363 var minimumY = Math.min.apply( Math, colGroup );
30364 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30366 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30368 // position the brick
30370 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30371 y: this.currentSize.y + minimumY + this.padHeight
30375 // apply setHeight to necessary columns
30376 var setHeight = minimumY + sz.height + this.padHeight;
30377 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30379 var setSpan = this.cols + 1 - colGroup.length;
30380 for ( var i = 0; i < setSpan; i++ ) {
30381 this.colYs[ shortColIndex + i ] = setHeight ;
30388 * @param {Number} colSpan - number of columns the element spans
30389 * @returns {Array} colGroup
30391 _getColGroup : function( colSpan )
30393 if ( colSpan < 2 ) {
30394 // if brick spans only one column, use all the column Ys
30399 // how many different places could this brick fit horizontally
30400 var groupCount = this.cols + 1 - colSpan;
30401 // for each group potential horizontal position
30402 for ( var i = 0; i < groupCount; i++ ) {
30403 // make an array of colY values for that one group
30404 var groupColYs = this.colYs.slice( i, i + colSpan );
30405 // and get the max value of the array
30406 colGroup[i] = Math.max.apply( Math, groupColYs );
30411 _manageStamp : function( stamp )
30413 var stampSize = stamp.getSize();
30414 var offset = stamp.getBox();
30415 // get the columns that this stamp affects
30416 var firstX = this.isOriginLeft ? offset.x : offset.right;
30417 var lastX = firstX + stampSize.width;
30418 var firstCol = Math.floor( firstX / this.columnWidth );
30419 firstCol = Math.max( 0, firstCol );
30421 var lastCol = Math.floor( lastX / this.columnWidth );
30422 // lastCol should not go over if multiple of columnWidth #425
30423 lastCol -= lastX % this.columnWidth ? 0 : 1;
30424 lastCol = Math.min( this.cols - 1, lastCol );
30426 // set colYs to bottom of the stamp
30427 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30430 for ( var i = firstCol; i <= lastCol; i++ ) {
30431 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30436 _getContainerSize : function()
30438 this.maxY = Math.max.apply( Math, this.colYs );
30443 if ( this.isFitWidth ) {
30444 size.width = this._getContainerFitWidth();
30450 _getContainerFitWidth : function()
30452 var unusedCols = 0;
30453 // count unused columns
30456 if ( this.colYs[i] !== 0 ) {
30461 // fit container to columns that have been used
30462 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30465 needsResizeLayout : function()
30467 var previousWidth = this.containerWidth;
30468 this.getContainerWidth();
30469 return previousWidth !== this.containerWidth;
30484 * @class Roo.bootstrap.MasonryBrick
30485 * @extends Roo.bootstrap.Component
30486 * Bootstrap MasonryBrick class
30489 * Create a new MasonryBrick
30490 * @param {Object} config The config object
30493 Roo.bootstrap.MasonryBrick = function(config){
30494 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30500 * When a MasonryBrick is clcik
30501 * @param {Roo.bootstrap.MasonryBrick} this
30502 * @param {Roo.EventObject} e
30508 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30511 * @cfg {String} title
30515 * @cfg {String} html
30519 * @cfg {String} bgimage
30523 * @cfg {String} videourl
30527 * @cfg {String} cls
30531 * @cfg {String} href
30535 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30540 * @cfg {String} (center|bottom) placetitle
30544 getAutoCreate : function()
30546 var cls = 'masonry-brick';
30548 if(this.href.length){
30549 cls += ' masonry-brick-link';
30552 if(this.bgimage.length){
30553 cls += ' masonry-brick-image';
30557 cls += ' masonry-' + this.size + '-brick';
30560 if(this.placetitle.length){
30562 switch (this.placetitle) {
30564 cls += ' masonry-center-title';
30567 cls += ' masonry-bottom-title';
30574 if(!this.html.length && !this.bgimage.length){
30575 cls += ' masonry-center-title';
30578 if(!this.html.length && this.bgimage.length){
30579 cls += ' masonry-bottom-title';
30584 cls += ' ' + this.cls;
30588 tag: (this.href.length) ? 'a' : 'div',
30593 cls: 'masonry-brick-paragraph',
30599 if(this.href.length){
30600 cfg.href = this.href;
30603 var cn = cfg.cn[0].cn;
30605 if(this.title.length){
30608 cls: 'masonry-brick-title',
30613 if(this.html.length){
30616 cls: 'masonry-brick-text',
30620 if (!this.title.length && !this.html.length) {
30621 cfg.cn[0].cls += ' hide';
30624 if(this.bgimage.length){
30627 cls: 'masonry-brick-image-view',
30631 if(this.videourl.length){
30632 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30633 // youtube support only?
30636 cls: 'masonry-brick-image-view',
30639 allowfullscreen : true
30648 initEvents: function()
30650 switch (this.size) {
30652 // this.intSize = 1;
30657 // this.intSize = 2;
30664 // this.intSize = 3;
30669 // this.intSize = 3;
30674 // this.intSize = 3;
30679 // this.intSize = 3;
30691 this.el.on('touchstart', this.onTouchStart, this);
30692 this.el.on('touchmove', this.onTouchMove, this);
30693 this.el.on('touchend', this.onTouchEnd, this);
30694 this.el.on('contextmenu', this.onContextMenu, this);
30696 this.el.on('mouseenter' ,this.enter, this);
30697 this.el.on('mouseleave', this.leave, this);
30700 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30701 this.parent().bricks.push(this);
30706 onClick: function(e, el)
30712 var time = this.endTimer - this.startTimer;
30720 e.preventDefault();
30723 enter: function(e, el)
30725 e.preventDefault();
30727 if(this.bgimage.length && this.html.length){
30728 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30732 leave: function(e, el)
30734 e.preventDefault();
30736 if(this.bgimage.length && this.html.length){
30737 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30741 onTouchStart: function(e, el)
30743 // e.preventDefault();
30745 this.touchmoved = false;
30747 if(!this.bgimage.length || !this.html.length){
30751 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30753 this.timer = new Date().getTime();
30757 onTouchMove: function(e, el)
30759 this.touchmoved = true;
30762 onContextMenu : function(e,el)
30764 e.preventDefault();
30765 e.stopPropagation();
30769 onTouchEnd: function(e, el)
30771 // e.preventDefault();
30773 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30780 if(!this.bgimage.length || !this.html.length){
30782 if(this.href.length){
30783 window.location.href = this.href;
30789 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30791 window.location.href = this.href;
30806 * @class Roo.bootstrap.Brick
30807 * @extends Roo.bootstrap.Component
30808 * Bootstrap Brick class
30811 * Create a new Brick
30812 * @param {Object} config The config object
30815 Roo.bootstrap.Brick = function(config){
30816 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30822 * When a Brick is click
30823 * @param {Roo.bootstrap.Brick} this
30824 * @param {Roo.EventObject} e
30830 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
30833 * @cfg {String} title
30837 * @cfg {String} html
30841 * @cfg {String} bgimage
30845 * @cfg {String} cls
30849 * @cfg {String} href
30853 * @cfg {String} video
30857 * @cfg {Boolean} square
30861 getAutoCreate : function()
30863 var cls = 'roo-brick';
30865 if(this.href.length){
30866 cls += ' roo-brick-link';
30869 if(this.bgimage.length){
30870 cls += ' roo-brick-image';
30873 if(!this.html.length && !this.bgimage.length){
30874 cls += ' roo-brick-center-title';
30877 if(!this.html.length && this.bgimage.length){
30878 cls += ' roo-brick-bottom-title';
30882 cls += ' ' + this.cls;
30886 tag: (this.href.length) ? 'a' : 'div',
30891 cls: 'roo-brick-paragraph',
30897 if(this.href.length){
30898 cfg.href = this.href;
30901 var cn = cfg.cn[0].cn;
30903 if(this.title.length){
30906 cls: 'roo-brick-title',
30911 if(this.html.length){
30914 cls: 'roo-brick-text',
30921 if(this.bgimage.length){
30924 cls: 'roo-brick-image-view',
30932 initEvents: function()
30934 if(this.title.length || this.html.length){
30935 this.el.on('mouseenter' ,this.enter, this);
30936 this.el.on('mouseleave', this.leave, this);
30940 Roo.EventManager.onWindowResize(this.resize, this);
30945 resize : function()
30947 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30949 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30950 // paragraph.setHeight(paragraph.getWidth());
30952 if(this.bgimage.length){
30953 var image = this.el.select('.roo-brick-image-view', true).first();
30954 image.setWidth(paragraph.getWidth());
30955 image.setHeight(paragraph.getWidth());
30960 enter: function(e, el)
30962 e.preventDefault();
30964 if(this.bgimage.length){
30965 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30966 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30970 leave: function(e, el)
30972 e.preventDefault();
30974 if(this.bgimage.length){
30975 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30976 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30986 * Ext JS Library 1.1.1
30987 * Copyright(c) 2006-2007, Ext JS, LLC.
30989 * Originally Released Under LGPL - original licence link has changed is not relivant.
30992 * <script type="text/javascript">
30997 * @class Roo.bootstrap.SplitBar
30998 * @extends Roo.util.Observable
30999 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31003 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31004 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31005 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31006 split.minSize = 100;
31007 split.maxSize = 600;
31008 split.animate = true;
31009 split.on('moved', splitterMoved);
31012 * Create a new SplitBar
31013 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
31014 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
31015 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31016 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
31017 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31018 position of the SplitBar).
31020 Roo.bootstrap.SplitBar = function(cfg){
31025 // dragElement : elm
31026 // resizingElement: el,
31028 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31029 // placement : Roo.bootstrap.SplitBar.LEFT ,
31030 // existingProxy ???
31033 this.el = Roo.get(cfg.dragElement, true);
31034 this.el.dom.unselectable = "on";
31036 this.resizingEl = Roo.get(cfg.resizingElement, true);
31040 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31041 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31044 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31047 * The minimum size of the resizing element. (Defaults to 0)
31053 * The maximum size of the resizing element. (Defaults to 2000)
31056 this.maxSize = 2000;
31059 * Whether to animate the transition to the new size
31062 this.animate = false;
31065 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31068 this.useShim = false;
31073 if(!cfg.existingProxy){
31075 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31077 this.proxy = Roo.get(cfg.existingProxy).dom;
31080 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31083 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31086 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31089 this.dragSpecs = {};
31092 * @private The adapter to use to positon and resize elements
31094 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31095 this.adapter.init(this);
31097 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31099 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31100 this.el.addClass("roo-splitbar-h");
31103 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31104 this.el.addClass("roo-splitbar-v");
31110 * Fires when the splitter is moved (alias for {@link #event-moved})
31111 * @param {Roo.bootstrap.SplitBar} this
31112 * @param {Number} newSize the new width or height
31117 * Fires when the splitter is moved
31118 * @param {Roo.bootstrap.SplitBar} this
31119 * @param {Number} newSize the new width or height
31123 * @event beforeresize
31124 * Fires before the splitter is dragged
31125 * @param {Roo.bootstrap.SplitBar} this
31127 "beforeresize" : true,
31129 "beforeapply" : true
31132 Roo.util.Observable.call(this);
31135 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31136 onStartProxyDrag : function(x, y){
31137 this.fireEvent("beforeresize", this);
31139 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
31141 o.enableDisplayMode("block");
31142 // all splitbars share the same overlay
31143 Roo.bootstrap.SplitBar.prototype.overlay = o;
31145 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31146 this.overlay.show();
31147 Roo.get(this.proxy).setDisplayed("block");
31148 var size = this.adapter.getElementSize(this);
31149 this.activeMinSize = this.getMinimumSize();;
31150 this.activeMaxSize = this.getMaximumSize();;
31151 var c1 = size - this.activeMinSize;
31152 var c2 = Math.max(this.activeMaxSize - size, 0);
31153 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31154 this.dd.resetConstraints();
31155 this.dd.setXConstraint(
31156 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
31157 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31159 this.dd.setYConstraint(0, 0);
31161 this.dd.resetConstraints();
31162 this.dd.setXConstraint(0, 0);
31163 this.dd.setYConstraint(
31164 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
31165 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31168 this.dragSpecs.startSize = size;
31169 this.dragSpecs.startPoint = [x, y];
31170 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31174 * @private Called after the drag operation by the DDProxy
31176 onEndProxyDrag : function(e){
31177 Roo.get(this.proxy).setDisplayed(false);
31178 var endPoint = Roo.lib.Event.getXY(e);
31180 this.overlay.hide();
31183 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31184 newSize = this.dragSpecs.startSize +
31185 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31186 endPoint[0] - this.dragSpecs.startPoint[0] :
31187 this.dragSpecs.startPoint[0] - endPoint[0]
31190 newSize = this.dragSpecs.startSize +
31191 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31192 endPoint[1] - this.dragSpecs.startPoint[1] :
31193 this.dragSpecs.startPoint[1] - endPoint[1]
31196 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31197 if(newSize != this.dragSpecs.startSize){
31198 if(this.fireEvent('beforeapply', this, newSize) !== false){
31199 this.adapter.setElementSize(this, newSize);
31200 this.fireEvent("moved", this, newSize);
31201 this.fireEvent("resize", this, newSize);
31207 * Get the adapter this SplitBar uses
31208 * @return The adapter object
31210 getAdapter : function(){
31211 return this.adapter;
31215 * Set the adapter this SplitBar uses
31216 * @param {Object} adapter A SplitBar adapter object
31218 setAdapter : function(adapter){
31219 this.adapter = adapter;
31220 this.adapter.init(this);
31224 * Gets the minimum size for the resizing element
31225 * @return {Number} The minimum size
31227 getMinimumSize : function(){
31228 return this.minSize;
31232 * Sets the minimum size for the resizing element
31233 * @param {Number} minSize The minimum size
31235 setMinimumSize : function(minSize){
31236 this.minSize = minSize;
31240 * Gets the maximum size for the resizing element
31241 * @return {Number} The maximum size
31243 getMaximumSize : function(){
31244 return this.maxSize;
31248 * Sets the maximum size for the resizing element
31249 * @param {Number} maxSize The maximum size
31251 setMaximumSize : function(maxSize){
31252 this.maxSize = maxSize;
31256 * Sets the initialize size for the resizing element
31257 * @param {Number} size The initial size
31259 setCurrentSize : function(size){
31260 var oldAnimate = this.animate;
31261 this.animate = false;
31262 this.adapter.setElementSize(this, size);
31263 this.animate = oldAnimate;
31267 * Destroy this splitbar.
31268 * @param {Boolean} removeEl True to remove the element
31270 destroy : function(removeEl){
31272 this.shim.remove();
31275 this.proxy.parentNode.removeChild(this.proxy);
31283 * @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.
31285 Roo.bootstrap.SplitBar.createProxy = function(dir){
31286 var proxy = new Roo.Element(document.createElement("div"));
31287 proxy.unselectable();
31288 var cls = 'roo-splitbar-proxy';
31289 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31290 document.body.appendChild(proxy.dom);
31295 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31296 * Default Adapter. It assumes the splitter and resizing element are not positioned
31297 * elements and only gets/sets the width of the element. Generally used for table based layouts.
31299 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31302 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31303 // do nothing for now
31304 init : function(s){
31308 * Called before drag operations to get the current size of the resizing element.
31309 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31311 getElementSize : function(s){
31312 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31313 return s.resizingEl.getWidth();
31315 return s.resizingEl.getHeight();
31320 * Called after drag operations to set the size of the resizing element.
31321 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31322 * @param {Number} newSize The new size to set
31323 * @param {Function} onComplete A function to be invoked when resizing is complete
31325 setElementSize : function(s, newSize, onComplete){
31326 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31328 s.resizingEl.setWidth(newSize);
31330 onComplete(s, newSize);
31333 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31338 s.resizingEl.setHeight(newSize);
31340 onComplete(s, newSize);
31343 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31350 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31351 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31352 * Adapter that moves the splitter element to align with the resized sizing element.
31353 * Used with an absolute positioned SplitBar.
31354 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31355 * document.body, make sure you assign an id to the body element.
31357 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31358 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31359 this.container = Roo.get(container);
31362 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31363 init : function(s){
31364 this.basic.init(s);
31367 getElementSize : function(s){
31368 return this.basic.getElementSize(s);
31371 setElementSize : function(s, newSize, onComplete){
31372 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31375 moveSplitter : function(s){
31376 var yes = Roo.bootstrap.SplitBar;
31377 switch(s.placement){
31379 s.el.setX(s.resizingEl.getRight());
31382 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31385 s.el.setY(s.resizingEl.getBottom());
31388 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31395 * Orientation constant - Create a vertical SplitBar
31399 Roo.bootstrap.SplitBar.VERTICAL = 1;
31402 * Orientation constant - Create a horizontal SplitBar
31406 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31409 * Placement constant - The resizing element is to the left of the splitter element
31413 Roo.bootstrap.SplitBar.LEFT = 1;
31416 * Placement constant - The resizing element is to the right of the splitter element
31420 Roo.bootstrap.SplitBar.RIGHT = 2;
31423 * Placement constant - The resizing element is positioned above the splitter element
31427 Roo.bootstrap.SplitBar.TOP = 3;
31430 * Placement constant - The resizing element is positioned under splitter element
31434 Roo.bootstrap.SplitBar.BOTTOM = 4;
31435 Roo.namespace("Roo.bootstrap.layout");/*
31437 * Ext JS Library 1.1.1
31438 * Copyright(c) 2006-2007, Ext JS, LLC.
31440 * Originally Released Under LGPL - original licence link has changed is not relivant.
31443 * <script type="text/javascript">
31447 * @class Roo.bootstrap.layout.Manager
31448 * @extends Roo.bootstrap.Component
31449 * Base class for layout managers.
31451 Roo.bootstrap.layout.Manager = function(config)
31453 Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31459 /** false to disable window resize monitoring @type Boolean */
31460 this.monitorWindowResize = true;
31465 * Fires when a layout is performed.
31466 * @param {Roo.LayoutManager} this
31470 * @event regionresized
31471 * Fires when the user resizes a region.
31472 * @param {Roo.LayoutRegion} region The resized region
31473 * @param {Number} newSize The new size (width for east/west, height for north/south)
31475 "regionresized" : true,
31477 * @event regioncollapsed
31478 * Fires when a region is collapsed.
31479 * @param {Roo.LayoutRegion} region The collapsed region
31481 "regioncollapsed" : true,
31483 * @event regionexpanded
31484 * Fires when a region is expanded.
31485 * @param {Roo.LayoutRegion} region The expanded region
31487 "regionexpanded" : true
31489 this.updating = false;
31492 this.el = Roo.get(config.el);
31498 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31503 monitorWindowResize : true,
31509 onRender : function(ct, position)
31512 this.el = Roo.get(ct);
31518 initEvents: function()
31522 // ie scrollbar fix
31523 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31524 document.body.scroll = "no";
31525 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31526 this.el.position('relative');
31528 this.id = this.el.id;
31529 this.el.addClass("roo-layout-container");
31530 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31531 if(this.el.dom != document.body ) {
31532 this.el.on('resize', this.layout,this);
31533 this.el.on('show', this.layout,this);
31539 * Returns true if this layout is currently being updated
31540 * @return {Boolean}
31542 isUpdating : function(){
31543 return this.updating;
31547 * Suspend the LayoutManager from doing auto-layouts while
31548 * making multiple add or remove calls
31550 beginUpdate : function(){
31551 this.updating = true;
31555 * Restore auto-layouts and optionally disable the manager from performing a layout
31556 * @param {Boolean} noLayout true to disable a layout update
31558 endUpdate : function(noLayout){
31559 this.updating = false;
31565 layout: function(){
31569 onRegionResized : function(region, newSize){
31570 this.fireEvent("regionresized", region, newSize);
31574 onRegionCollapsed : function(region){
31575 this.fireEvent("regioncollapsed", region);
31578 onRegionExpanded : function(region){
31579 this.fireEvent("regionexpanded", region);
31583 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31584 * performs box-model adjustments.
31585 * @return {Object} The size as an object {width: (the width), height: (the height)}
31587 getViewSize : function()
31590 if(this.el.dom != document.body){
31591 size = this.el.getSize();
31593 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31595 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31596 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31601 * Returns the Element this layout is bound to.
31602 * @return {Roo.Element}
31604 getEl : function(){
31609 * Returns the specified region.
31610 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31611 * @return {Roo.LayoutRegion}
31613 getRegion : function(target){
31614 return this.regions[target.toLowerCase()];
31617 onWindowResize : function(){
31618 if(this.monitorWindowResize){
31624 * Ext JS Library 1.1.1
31625 * Copyright(c) 2006-2007, Ext JS, LLC.
31627 * Originally Released Under LGPL - original licence link has changed is not relivant.
31630 * <script type="text/javascript">
31633 * @class Roo.bootstrap.layout.Border
31634 * @extends Roo.bootstrap.layout.Manager
31635 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31636 * please see: examples/bootstrap/nested.html<br><br>
31638 <b>The container the layout is rendered into can be either the body element or any other element.
31639 If it is not the body element, the container needs to either be an absolute positioned element,
31640 or you will need to add "position:relative" to the css of the container. You will also need to specify
31641 the container size if it is not the body element.</b>
31644 * Create a new Border
31645 * @param {Object} config Configuration options
31647 Roo.bootstrap.layout.Border = function(config){
31648 config = config || {};
31649 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31653 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31654 if(config[region]){
31655 config[region].region = region;
31656 this.addRegion(config[region]);
31662 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
31664 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31666 * Creates and adds a new region if it doesn't already exist.
31667 * @param {String} target The target region key (north, south, east, west or center).
31668 * @param {Object} config The regions config object
31669 * @return {BorderLayoutRegion} The new region
31671 addRegion : function(config)
31673 if(!this.regions[config.region]){
31674 var r = this.factory(config);
31675 this.bindRegion(r);
31677 return this.regions[config.region];
31681 bindRegion : function(r){
31682 this.regions[r.config.region] = r;
31684 r.on("visibilitychange", this.layout, this);
31685 r.on("paneladded", this.layout, this);
31686 r.on("panelremoved", this.layout, this);
31687 r.on("invalidated", this.layout, this);
31688 r.on("resized", this.onRegionResized, this);
31689 r.on("collapsed", this.onRegionCollapsed, this);
31690 r.on("expanded", this.onRegionExpanded, this);
31694 * Performs a layout update.
31696 layout : function()
31698 if(this.updating) {
31701 var size = this.getViewSize();
31702 var w = size.width;
31703 var h = size.height;
31708 //var x = 0, y = 0;
31710 var rs = this.regions;
31711 var north = rs["north"];
31712 var south = rs["south"];
31713 var west = rs["west"];
31714 var east = rs["east"];
31715 var center = rs["center"];
31716 //if(this.hideOnLayout){ // not supported anymore
31717 //c.el.setStyle("display", "none");
31719 if(north && north.isVisible()){
31720 var b = north.getBox();
31721 var m = north.getMargins();
31722 b.width = w - (m.left+m.right);
31725 centerY = b.height + b.y + m.bottom;
31726 centerH -= centerY;
31727 north.updateBox(this.safeBox(b));
31729 if(south && south.isVisible()){
31730 var b = south.getBox();
31731 var m = south.getMargins();
31732 b.width = w - (m.left+m.right);
31734 var totalHeight = (b.height + m.top + m.bottom);
31735 b.y = h - totalHeight + m.top;
31736 centerH -= totalHeight;
31737 south.updateBox(this.safeBox(b));
31739 if(west && west.isVisible()){
31740 var b = west.getBox();
31741 var m = west.getMargins();
31742 b.height = centerH - (m.top+m.bottom);
31744 b.y = centerY + m.top;
31745 var totalWidth = (b.width + m.left + m.right);
31746 centerX += totalWidth;
31747 centerW -= totalWidth;
31748 west.updateBox(this.safeBox(b));
31750 if(east && east.isVisible()){
31751 var b = east.getBox();
31752 var m = east.getMargins();
31753 b.height = centerH - (m.top+m.bottom);
31754 var totalWidth = (b.width + m.left + m.right);
31755 b.x = w - totalWidth + m.left;
31756 b.y = centerY + m.top;
31757 centerW -= totalWidth;
31758 east.updateBox(this.safeBox(b));
31761 var m = center.getMargins();
31763 x: centerX + m.left,
31764 y: centerY + m.top,
31765 width: centerW - (m.left+m.right),
31766 height: centerH - (m.top+m.bottom)
31768 //if(this.hideOnLayout){
31769 //center.el.setStyle("display", "block");
31771 center.updateBox(this.safeBox(centerBox));
31774 this.fireEvent("layout", this);
31778 safeBox : function(box){
31779 box.width = Math.max(0, box.width);
31780 box.height = Math.max(0, box.height);
31785 * Adds a ContentPanel (or subclass) to this layout.
31786 * @param {String} target The target region key (north, south, east, west or center).
31787 * @param {Roo.ContentPanel} panel The panel to add
31788 * @return {Roo.ContentPanel} The added panel
31790 add : function(target, panel){
31792 target = target.toLowerCase();
31793 return this.regions[target].add(panel);
31797 * Remove a ContentPanel (or subclass) to this layout.
31798 * @param {String} target The target region key (north, south, east, west or center).
31799 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31800 * @return {Roo.ContentPanel} The removed panel
31802 remove : function(target, panel){
31803 target = target.toLowerCase();
31804 return this.regions[target].remove(panel);
31808 * Searches all regions for a panel with the specified id
31809 * @param {String} panelId
31810 * @return {Roo.ContentPanel} The panel or null if it wasn't found
31812 findPanel : function(panelId){
31813 var rs = this.regions;
31814 for(var target in rs){
31815 if(typeof rs[target] != "function"){
31816 var p = rs[target].getPanel(panelId);
31826 * Searches all regions for a panel with the specified id and activates (shows) it.
31827 * @param {String/ContentPanel} panelId The panels id or the panel itself
31828 * @return {Roo.ContentPanel} The shown panel or null
31830 showPanel : function(panelId) {
31831 var rs = this.regions;
31832 for(var target in rs){
31833 var r = rs[target];
31834 if(typeof r != "function"){
31835 if(r.hasPanel(panelId)){
31836 return r.showPanel(panelId);
31844 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31845 * @param {Roo.state.Provider} provider (optional) An alternate state provider
31848 restoreState : function(provider){
31850 provider = Roo.state.Manager;
31852 var sm = new Roo.LayoutStateManager();
31853 sm.init(this, provider);
31859 * Adds a xtype elements to the layout.
31863 xtype : 'ContentPanel',
31870 xtype : 'NestedLayoutPanel',
31876 items : [ ... list of content panels or nested layout panels.. ]
31880 * @param {Object} cfg Xtype definition of item to add.
31882 addxtype : function(cfg)
31884 // basically accepts a pannel...
31885 // can accept a layout region..!?!?
31886 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31889 // theory? children can only be panels??
31891 //if (!cfg.xtype.match(/Panel$/)) {
31896 if (typeof(cfg.region) == 'undefined') {
31897 Roo.log("Failed to add Panel, region was not set");
31901 var region = cfg.region;
31907 xitems = cfg.items;
31914 case 'Content': // ContentPanel (el, cfg)
31915 case 'Scroll': // ContentPanel (el, cfg)
31917 cfg.autoCreate = true;
31918 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31920 // var el = this.el.createChild();
31921 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31924 this.add(region, ret);
31928 case 'TreePanel': // our new panel!
31929 cfg.el = this.el.createChild();
31930 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31931 this.add(region, ret);
31936 // create a new Layout (which is a Border Layout...
31938 var clayout = cfg.layout;
31939 clayout.el = this.el.createChild();
31940 clayout.items = clayout.items || [];
31944 // replace this exitems with the clayout ones..
31945 xitems = clayout.items;
31947 // force background off if it's in center...
31948 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31949 cfg.background = false;
31951 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
31954 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31955 //console.log('adding nested layout panel ' + cfg.toSource());
31956 this.add(region, ret);
31957 nb = {}; /// find first...
31962 // needs grid and region
31964 //var el = this.getRegion(region).el.createChild();
31966 *var el = this.el.createChild();
31967 // create the grid first...
31968 cfg.grid.container = el;
31969 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31972 if (region == 'center' && this.active ) {
31973 cfg.background = false;
31976 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31978 this.add(region, ret);
31980 if (cfg.background) {
31981 // render grid on panel activation (if panel background)
31982 ret.on('activate', function(gp) {
31983 if (!gp.grid.rendered) {
31984 // gp.grid.render(el);
31988 // cfg.grid.render(el);
31994 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31995 // it was the old xcomponent building that caused this before.
31996 // espeically if border is the top element in the tree.
32006 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32008 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32009 this.add(region, ret);
32013 throw "Can not add '" + cfg.xtype + "' to Border";
32019 this.beginUpdate();
32023 Roo.each(xitems, function(i) {
32024 region = nb && i.region ? i.region : false;
32026 var add = ret.addxtype(i);
32029 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32030 if (!i.background) {
32031 abn[region] = nb[region] ;
32038 // make the last non-background panel active..
32039 //if (nb) { Roo.log(abn); }
32042 for(var r in abn) {
32043 region = this.getRegion(r);
32045 // tried using nb[r], but it does not work..
32047 region.showPanel(abn[r]);
32058 factory : function(cfg)
32061 var validRegions = Roo.bootstrap.layout.Border.regions;
32063 var target = cfg.region;
32066 var r = Roo.bootstrap.layout;
32070 return new r.North(cfg);
32072 return new r.South(cfg);
32074 return new r.East(cfg);
32076 return new r.West(cfg);
32078 return new r.Center(cfg);
32080 throw 'Layout region "'+target+'" not supported.';
32087 * Ext JS Library 1.1.1
32088 * Copyright(c) 2006-2007, Ext JS, LLC.
32090 * Originally Released Under LGPL - original licence link has changed is not relivant.
32093 * <script type="text/javascript">
32097 * @class Roo.bootstrap.layout.Basic
32098 * @extends Roo.util.Observable
32099 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32100 * and does not have a titlebar, tabs or any other features. All it does is size and position
32101 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32102 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32103 * @cfg {string} region the region that it inhabits..
32104 * @cfg {bool} skipConfig skip config?
32108 Roo.bootstrap.layout.Basic = function(config){
32110 this.mgr = config.mgr;
32112 this.position = config.region;
32114 var skipConfig = config.skipConfig;
32118 * @scope Roo.BasicLayoutRegion
32122 * @event beforeremove
32123 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32124 * @param {Roo.LayoutRegion} this
32125 * @param {Roo.ContentPanel} panel The panel
32126 * @param {Object} e The cancel event object
32128 "beforeremove" : true,
32130 * @event invalidated
32131 * Fires when the layout for this region is changed.
32132 * @param {Roo.LayoutRegion} this
32134 "invalidated" : true,
32136 * @event visibilitychange
32137 * Fires when this region is shown or hidden
32138 * @param {Roo.LayoutRegion} this
32139 * @param {Boolean} visibility true or false
32141 "visibilitychange" : true,
32143 * @event paneladded
32144 * Fires when a panel is added.
32145 * @param {Roo.LayoutRegion} this
32146 * @param {Roo.ContentPanel} panel The panel
32148 "paneladded" : true,
32150 * @event panelremoved
32151 * Fires when a panel is removed.
32152 * @param {Roo.LayoutRegion} this
32153 * @param {Roo.ContentPanel} panel The panel
32155 "panelremoved" : true,
32157 * @event beforecollapse
32158 * Fires when this region before collapse.
32159 * @param {Roo.LayoutRegion} this
32161 "beforecollapse" : true,
32164 * Fires when this region is collapsed.
32165 * @param {Roo.LayoutRegion} this
32167 "collapsed" : true,
32170 * Fires when this region is expanded.
32171 * @param {Roo.LayoutRegion} this
32176 * Fires when this region is slid into view.
32177 * @param {Roo.LayoutRegion} this
32179 "slideshow" : true,
32182 * Fires when this region slides out of view.
32183 * @param {Roo.LayoutRegion} this
32185 "slidehide" : true,
32187 * @event panelactivated
32188 * Fires when a panel is activated.
32189 * @param {Roo.LayoutRegion} this
32190 * @param {Roo.ContentPanel} panel The activated panel
32192 "panelactivated" : true,
32195 * Fires when the user resizes this region.
32196 * @param {Roo.LayoutRegion} this
32197 * @param {Number} newSize The new size (width for east/west, height for north/south)
32201 /** A collection of panels in this region. @type Roo.util.MixedCollection */
32202 this.panels = new Roo.util.MixedCollection();
32203 this.panels.getKey = this.getPanelId.createDelegate(this);
32205 this.activePanel = null;
32206 // ensure listeners are added...
32208 if (config.listeners || config.events) {
32209 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32210 listeners : config.listeners || {},
32211 events : config.events || {}
32215 if(skipConfig !== true){
32216 this.applyConfig(config);
32220 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32222 getPanelId : function(p){
32226 applyConfig : function(config){
32227 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32228 this.config = config;
32233 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
32234 * the width, for horizontal (north, south) the height.
32235 * @param {Number} newSize The new width or height
32237 resizeTo : function(newSize){
32238 var el = this.el ? this.el :
32239 (this.activePanel ? this.activePanel.getEl() : null);
32241 switch(this.position){
32244 el.setWidth(newSize);
32245 this.fireEvent("resized", this, newSize);
32249 el.setHeight(newSize);
32250 this.fireEvent("resized", this, newSize);
32256 getBox : function(){
32257 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32260 getMargins : function(){
32261 return this.margins;
32264 updateBox : function(box){
32266 var el = this.activePanel.getEl();
32267 el.dom.style.left = box.x + "px";
32268 el.dom.style.top = box.y + "px";
32269 this.activePanel.setSize(box.width, box.height);
32273 * Returns the container element for this region.
32274 * @return {Roo.Element}
32276 getEl : function(){
32277 return this.activePanel;
32281 * Returns true if this region is currently visible.
32282 * @return {Boolean}
32284 isVisible : function(){
32285 return this.activePanel ? true : false;
32288 setActivePanel : function(panel){
32289 panel = this.getPanel(panel);
32290 if(this.activePanel && this.activePanel != panel){
32291 this.activePanel.setActiveState(false);
32292 this.activePanel.getEl().setLeftTop(-10000,-10000);
32294 this.activePanel = panel;
32295 panel.setActiveState(true);
32297 panel.setSize(this.box.width, this.box.height);
32299 this.fireEvent("panelactivated", this, panel);
32300 this.fireEvent("invalidated");
32304 * Show the specified panel.
32305 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32306 * @return {Roo.ContentPanel} The shown panel or null
32308 showPanel : function(panel){
32309 panel = this.getPanel(panel);
32311 this.setActivePanel(panel);
32317 * Get the active panel for this region.
32318 * @return {Roo.ContentPanel} The active panel or null
32320 getActivePanel : function(){
32321 return this.activePanel;
32325 * Add the passed ContentPanel(s)
32326 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32327 * @return {Roo.ContentPanel} The panel added (if only one was added)
32329 add : function(panel){
32330 if(arguments.length > 1){
32331 for(var i = 0, len = arguments.length; i < len; i++) {
32332 this.add(arguments[i]);
32336 if(this.hasPanel(panel)){
32337 this.showPanel(panel);
32340 var el = panel.getEl();
32341 if(el.dom.parentNode != this.mgr.el.dom){
32342 this.mgr.el.dom.appendChild(el.dom);
32344 if(panel.setRegion){
32345 panel.setRegion(this);
32347 this.panels.add(panel);
32348 el.setStyle("position", "absolute");
32349 if(!panel.background){
32350 this.setActivePanel(panel);
32351 if(this.config.initialSize && this.panels.getCount()==1){
32352 this.resizeTo(this.config.initialSize);
32355 this.fireEvent("paneladded", this, panel);
32360 * Returns true if the panel is in this region.
32361 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32362 * @return {Boolean}
32364 hasPanel : function(panel){
32365 if(typeof panel == "object"){ // must be panel obj
32366 panel = panel.getId();
32368 return this.getPanel(panel) ? true : false;
32372 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32373 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32374 * @param {Boolean} preservePanel Overrides the config preservePanel option
32375 * @return {Roo.ContentPanel} The panel that was removed
32377 remove : function(panel, preservePanel){
32378 panel = this.getPanel(panel);
32383 this.fireEvent("beforeremove", this, panel, e);
32384 if(e.cancel === true){
32387 var panelId = panel.getId();
32388 this.panels.removeKey(panelId);
32393 * Returns the panel specified or null if it's not in this region.
32394 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32395 * @return {Roo.ContentPanel}
32397 getPanel : function(id){
32398 if(typeof id == "object"){ // must be panel obj
32401 return this.panels.get(id);
32405 * Returns this regions position (north/south/east/west/center).
32408 getPosition: function(){
32409 return this.position;
32413 * Ext JS Library 1.1.1
32414 * Copyright(c) 2006-2007, Ext JS, LLC.
32416 * Originally Released Under LGPL - original licence link has changed is not relivant.
32419 * <script type="text/javascript">
32423 * @class Roo.bootstrap.layout.Region
32424 * @extends Roo.bootstrap.layout.Basic
32425 * This class represents a region in a layout manager.
32427 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32428 * @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})
32429 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
32430 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
32431 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
32432 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
32433 * @cfg {String} title The title for the region (overrides panel titles)
32434 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
32435 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32436 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
32437 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32438 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
32439 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32440 * the space available, similar to FireFox 1.5 tabs (defaults to false)
32441 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
32442 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
32443 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
32445 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
32446 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
32447 * @cfg {Boolean} disableTabTips True to disable tab tooltips
32448 * @cfg {Number} width For East/West panels
32449 * @cfg {Number} height For North/South panels
32450 * @cfg {Boolean} split To show the splitter
32451 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
32453 * @cfg {string} cls Extra CSS classes to add to region
32455 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32456 * @cfg {string} region the region that it inhabits..
32459 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
32460 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
32462 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
32463 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
32464 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
32466 Roo.bootstrap.layout.Region = function(config)
32468 this.applyConfig(config);
32470 var mgr = config.mgr;
32471 var pos = config.region;
32472 config.skipConfig = true;
32473 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32476 this.onRender(mgr.el);
32479 this.visible = true;
32480 this.collapsed = false;
32483 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32485 position: '', // set by wrapper (eg. north/south etc..)
32487 createBody : function(){
32488 /** This region's body element
32489 * @type Roo.Element */
32490 this.bodyEl = this.el.createChild({
32492 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32496 onRender: function(ctr, pos)
32498 var dh = Roo.DomHelper;
32499 /** This region's container element
32500 * @type Roo.Element */
32501 this.el = dh.append(ctr.dom, {
32503 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32505 /** This region's title element
32506 * @type Roo.Element */
32508 this.titleEl = dh.append(this.el.dom,
32511 unselectable: "on",
32512 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32514 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
32515 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32518 this.titleEl.enableDisplayMode();
32519 /** This region's title text element
32520 * @type HTMLElement */
32521 this.titleTextEl = this.titleEl.dom.firstChild;
32522 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32524 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32525 this.closeBtn.enableDisplayMode();
32526 this.closeBtn.on("click", this.closeClicked, this);
32527 this.closeBtn.hide();
32529 this.createBody(this.config);
32530 if(this.config.hideWhenEmpty){
32532 this.on("paneladded", this.validateVisibility, this);
32533 this.on("panelremoved", this.validateVisibility, this);
32535 if(this.autoScroll){
32536 this.bodyEl.setStyle("overflow", "auto");
32538 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32540 //if(c.titlebar !== false){
32541 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32542 this.titleEl.hide();
32544 this.titleEl.show();
32545 if(this.config.title){
32546 this.titleTextEl.innerHTML = this.config.title;
32550 if(this.config.collapsed){
32551 this.collapse(true);
32553 if(this.config.hidden){
32558 applyConfig : function(c)
32561 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32562 var dh = Roo.DomHelper;
32563 if(c.titlebar !== false){
32564 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32565 this.collapseBtn.on("click", this.collapse, this);
32566 this.collapseBtn.enableDisplayMode();
32568 if(c.showPin === true || this.showPin){
32569 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32570 this.stickBtn.enableDisplayMode();
32571 this.stickBtn.on("click", this.expand, this);
32572 this.stickBtn.hide();
32577 /** This region's collapsed element
32578 * @type Roo.Element */
32581 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32582 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32585 if(c.floatable !== false){
32586 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32587 this.collapsedEl.on("click", this.collapseClick, this);
32590 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32591 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32592 id: "message", unselectable: "on", style:{"float":"left"}});
32593 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32595 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32596 this.expandBtn.on("click", this.expand, this);
32600 if(this.collapseBtn){
32601 this.collapseBtn.setVisible(c.collapsible == true);
32604 this.cmargins = c.cmargins || this.cmargins ||
32605 (this.position == "west" || this.position == "east" ?
32606 {top: 0, left: 2, right:2, bottom: 0} :
32607 {top: 2, left: 0, right:0, bottom: 2});
32609 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32612 this.bottomTabs = c.tabPosition != "top";
32614 this.autoScroll = c.autoScroll || false;
32619 this.duration = c.duration || .30;
32620 this.slideDuration = c.slideDuration || .45;
32625 * Returns true if this region is currently visible.
32626 * @return {Boolean}
32628 isVisible : function(){
32629 return this.visible;
32633 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32634 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
32636 //setCollapsedTitle : function(title){
32637 // title = title || " ";
32638 // if(this.collapsedTitleTextEl){
32639 // this.collapsedTitleTextEl.innerHTML = title;
32643 getBox : function(){
32645 // if(!this.collapsed){
32646 b = this.el.getBox(false, true);
32648 // b = this.collapsedEl.getBox(false, true);
32653 getMargins : function(){
32654 return this.margins;
32655 //return this.collapsed ? this.cmargins : this.margins;
32658 highlight : function(){
32659 this.el.addClass("x-layout-panel-dragover");
32662 unhighlight : function(){
32663 this.el.removeClass("x-layout-panel-dragover");
32666 updateBox : function(box)
32669 if(!this.collapsed){
32670 this.el.dom.style.left = box.x + "px";
32671 this.el.dom.style.top = box.y + "px";
32672 this.updateBody(box.width, box.height);
32674 this.collapsedEl.dom.style.left = box.x + "px";
32675 this.collapsedEl.dom.style.top = box.y + "px";
32676 this.collapsedEl.setSize(box.width, box.height);
32679 this.tabs.autoSizeTabs();
32683 updateBody : function(w, h)
32686 this.el.setWidth(w);
32687 w -= this.el.getBorderWidth("rl");
32688 if(this.config.adjustments){
32689 w += this.config.adjustments[0];
32693 this.el.setHeight(h);
32694 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32695 h -= this.el.getBorderWidth("tb");
32696 if(this.config.adjustments){
32697 h += this.config.adjustments[1];
32699 this.bodyEl.setHeight(h);
32701 h = this.tabs.syncHeight(h);
32704 if(this.panelSize){
32705 w = w !== null ? w : this.panelSize.width;
32706 h = h !== null ? h : this.panelSize.height;
32708 if(this.activePanel){
32709 var el = this.activePanel.getEl();
32710 w = w !== null ? w : el.getWidth();
32711 h = h !== null ? h : el.getHeight();
32712 this.panelSize = {width: w, height: h};
32713 this.activePanel.setSize(w, h);
32715 if(Roo.isIE && this.tabs){
32716 this.tabs.el.repaint();
32721 * Returns the container element for this region.
32722 * @return {Roo.Element}
32724 getEl : function(){
32729 * Hides this region.
32732 //if(!this.collapsed){
32733 this.el.dom.style.left = "-2000px";
32736 // this.collapsedEl.dom.style.left = "-2000px";
32737 // this.collapsedEl.hide();
32739 this.visible = false;
32740 this.fireEvent("visibilitychange", this, false);
32744 * Shows this region if it was previously hidden.
32747 //if(!this.collapsed){
32750 // this.collapsedEl.show();
32752 this.visible = true;
32753 this.fireEvent("visibilitychange", this, true);
32756 closeClicked : function(){
32757 if(this.activePanel){
32758 this.remove(this.activePanel);
32762 collapseClick : function(e){
32764 e.stopPropagation();
32767 e.stopPropagation();
32773 * Collapses this region.
32774 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32777 collapse : function(skipAnim, skipCheck = false){
32778 if(this.collapsed) {
32782 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32784 this.collapsed = true;
32786 this.split.el.hide();
32788 if(this.config.animate && skipAnim !== true){
32789 this.fireEvent("invalidated", this);
32790 this.animateCollapse();
32792 this.el.setLocation(-20000,-20000);
32794 this.collapsedEl.show();
32795 this.fireEvent("collapsed", this);
32796 this.fireEvent("invalidated", this);
32802 animateCollapse : function(){
32807 * Expands this region if it was previously collapsed.
32808 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32809 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32812 expand : function(e, skipAnim){
32814 e.stopPropagation();
32816 if(!this.collapsed || this.el.hasActiveFx()) {
32820 this.afterSlideIn();
32823 this.collapsed = false;
32824 if(this.config.animate && skipAnim !== true){
32825 this.animateExpand();
32829 this.split.el.show();
32831 this.collapsedEl.setLocation(-2000,-2000);
32832 this.collapsedEl.hide();
32833 this.fireEvent("invalidated", this);
32834 this.fireEvent("expanded", this);
32838 animateExpand : function(){
32842 initTabs : function()
32844 this.bodyEl.setStyle("overflow", "hidden");
32845 var ts = new Roo.bootstrap.panel.Tabs({
32846 el: this.bodyEl.dom,
32847 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32848 disableTooltips: this.config.disableTabTips,
32849 toolbar : this.config.toolbar
32852 if(this.config.hideTabs){
32853 ts.stripWrap.setDisplayed(false);
32856 ts.resizeTabs = this.config.resizeTabs === true;
32857 ts.minTabWidth = this.config.minTabWidth || 40;
32858 ts.maxTabWidth = this.config.maxTabWidth || 250;
32859 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32860 ts.monitorResize = false;
32861 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32862 ts.bodyEl.addClass('roo-layout-tabs-body');
32863 this.panels.each(this.initPanelAsTab, this);
32866 initPanelAsTab : function(panel){
32867 var ti = this.tabs.addTab(
32869 panel.getTitle(), null,
32870 this.config.closeOnTab && panel.isClosable()
32872 if(panel.tabTip !== undefined){
32873 ti.setTooltip(panel.tabTip);
32875 ti.on("activate", function(){
32876 this.setActivePanel(panel);
32879 if(this.config.closeOnTab){
32880 ti.on("beforeclose", function(t, e){
32882 this.remove(panel);
32888 updatePanelTitle : function(panel, title)
32890 if(this.activePanel == panel){
32891 this.updateTitle(title);
32894 var ti = this.tabs.getTab(panel.getEl().id);
32896 if(panel.tabTip !== undefined){
32897 ti.setTooltip(panel.tabTip);
32902 updateTitle : function(title){
32903 if(this.titleTextEl && !this.config.title){
32904 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
32908 setActivePanel : function(panel)
32910 panel = this.getPanel(panel);
32911 if(this.activePanel && this.activePanel != panel){
32912 this.activePanel.setActiveState(false);
32914 this.activePanel = panel;
32915 panel.setActiveState(true);
32916 if(this.panelSize){
32917 panel.setSize(this.panelSize.width, this.panelSize.height);
32920 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32922 this.updateTitle(panel.getTitle());
32924 this.fireEvent("invalidated", this);
32926 this.fireEvent("panelactivated", this, panel);
32930 * Shows the specified panel.
32931 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32932 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32934 showPanel : function(panel)
32936 panel = this.getPanel(panel);
32939 var tab = this.tabs.getTab(panel.getEl().id);
32940 if(tab.isHidden()){
32941 this.tabs.unhideTab(tab.id);
32945 this.setActivePanel(panel);
32952 * Get the active panel for this region.
32953 * @return {Roo.ContentPanel} The active panel or null
32955 getActivePanel : function(){
32956 return this.activePanel;
32959 validateVisibility : function(){
32960 if(this.panels.getCount() < 1){
32961 this.updateTitle(" ");
32962 this.closeBtn.hide();
32965 if(!this.isVisible()){
32972 * Adds the passed ContentPanel(s) to this region.
32973 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32974 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32976 add : function(panel){
32977 if(arguments.length > 1){
32978 for(var i = 0, len = arguments.length; i < len; i++) {
32979 this.add(arguments[i]);
32983 if(this.hasPanel(panel)){
32984 this.showPanel(panel);
32987 panel.setRegion(this);
32988 this.panels.add(panel);
32989 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32990 this.bodyEl.dom.appendChild(panel.getEl().dom);
32991 if(panel.background !== true){
32992 this.setActivePanel(panel);
32994 this.fireEvent("paneladded", this, panel);
33000 this.initPanelAsTab(panel);
33004 if(panel.background !== true){
33005 this.tabs.activate(panel.getEl().id);
33007 this.fireEvent("paneladded", this, panel);
33012 * Hides the tab for the specified panel.
33013 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33015 hidePanel : function(panel){
33016 if(this.tabs && (panel = this.getPanel(panel))){
33017 this.tabs.hideTab(panel.getEl().id);
33022 * Unhides the tab for a previously hidden panel.
33023 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33025 unhidePanel : function(panel){
33026 if(this.tabs && (panel = this.getPanel(panel))){
33027 this.tabs.unhideTab(panel.getEl().id);
33031 clearPanels : function(){
33032 while(this.panels.getCount() > 0){
33033 this.remove(this.panels.first());
33038 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33039 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33040 * @param {Boolean} preservePanel Overrides the config preservePanel option
33041 * @return {Roo.ContentPanel} The panel that was removed
33043 remove : function(panel, preservePanel)
33045 panel = this.getPanel(panel);
33050 this.fireEvent("beforeremove", this, panel, e);
33051 if(e.cancel === true){
33054 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33055 var panelId = panel.getId();
33056 this.panels.removeKey(panelId);
33058 document.body.appendChild(panel.getEl().dom);
33061 this.tabs.removeTab(panel.getEl().id);
33062 }else if (!preservePanel){
33063 this.bodyEl.dom.removeChild(panel.getEl().dom);
33065 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33066 var p = this.panels.first();
33067 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33068 tempEl.appendChild(p.getEl().dom);
33069 this.bodyEl.update("");
33070 this.bodyEl.dom.appendChild(p.getEl().dom);
33072 this.updateTitle(p.getTitle());
33074 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33075 this.setActivePanel(p);
33077 panel.setRegion(null);
33078 if(this.activePanel == panel){
33079 this.activePanel = null;
33081 if(this.config.autoDestroy !== false && preservePanel !== true){
33082 try{panel.destroy();}catch(e){}
33084 this.fireEvent("panelremoved", this, panel);
33089 * Returns the TabPanel component used by this region
33090 * @return {Roo.TabPanel}
33092 getTabs : function(){
33096 createTool : function(parentEl, className){
33097 var btn = Roo.DomHelper.append(parentEl, {
33099 cls: "x-layout-tools-button",
33102 cls: "roo-layout-tools-button-inner " + className,
33106 btn.addClassOnOver("roo-layout-tools-button-over");
33111 * Ext JS Library 1.1.1
33112 * Copyright(c) 2006-2007, Ext JS, LLC.
33114 * Originally Released Under LGPL - original licence link has changed is not relivant.
33117 * <script type="text/javascript">
33123 * @class Roo.SplitLayoutRegion
33124 * @extends Roo.LayoutRegion
33125 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33127 Roo.bootstrap.layout.Split = function(config){
33128 this.cursor = config.cursor;
33129 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33132 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33134 splitTip : "Drag to resize.",
33135 collapsibleSplitTip : "Drag to resize. Double click to hide.",
33136 useSplitTips : false,
33138 applyConfig : function(config){
33139 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33142 onRender : function(ctr,pos) {
33144 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33145 if(!this.config.split){
33150 var splitEl = Roo.DomHelper.append(ctr.dom, {
33152 id: this.el.id + "-split",
33153 cls: "roo-layout-split roo-layout-split-"+this.position,
33156 /** The SplitBar for this region
33157 * @type Roo.SplitBar */
33158 // does not exist yet...
33159 Roo.log([this.position, this.orientation]);
33161 this.split = new Roo.bootstrap.SplitBar({
33162 dragElement : splitEl,
33163 resizingElement: this.el,
33164 orientation : this.orientation
33167 this.split.on("moved", this.onSplitMove, this);
33168 this.split.useShim = this.config.useShim === true;
33169 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33170 if(this.useSplitTips){
33171 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33173 //if(config.collapsible){
33174 // this.split.el.on("dblclick", this.collapse, this);
33177 if(typeof this.config.minSize != "undefined"){
33178 this.split.minSize = this.config.minSize;
33180 if(typeof this.config.maxSize != "undefined"){
33181 this.split.maxSize = this.config.maxSize;
33183 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33184 this.hideSplitter();
33189 getHMaxSize : function(){
33190 var cmax = this.config.maxSize || 10000;
33191 var center = this.mgr.getRegion("center");
33192 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33195 getVMaxSize : function(){
33196 var cmax = this.config.maxSize || 10000;
33197 var center = this.mgr.getRegion("center");
33198 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33201 onSplitMove : function(split, newSize){
33202 this.fireEvent("resized", this, newSize);
33206 * Returns the {@link Roo.SplitBar} for this region.
33207 * @return {Roo.SplitBar}
33209 getSplitBar : function(){
33214 this.hideSplitter();
33215 Roo.bootstrap.layout.Split.superclass.hide.call(this);
33218 hideSplitter : function(){
33220 this.split.el.setLocation(-2000,-2000);
33221 this.split.el.hide();
33227 this.split.el.show();
33229 Roo.bootstrap.layout.Split.superclass.show.call(this);
33232 beforeSlide: function(){
33233 if(Roo.isGecko){// firefox overflow auto bug workaround
33234 this.bodyEl.clip();
33236 this.tabs.bodyEl.clip();
33238 if(this.activePanel){
33239 this.activePanel.getEl().clip();
33241 if(this.activePanel.beforeSlide){
33242 this.activePanel.beforeSlide();
33248 afterSlide : function(){
33249 if(Roo.isGecko){// firefox overflow auto bug workaround
33250 this.bodyEl.unclip();
33252 this.tabs.bodyEl.unclip();
33254 if(this.activePanel){
33255 this.activePanel.getEl().unclip();
33256 if(this.activePanel.afterSlide){
33257 this.activePanel.afterSlide();
33263 initAutoHide : function(){
33264 if(this.autoHide !== false){
33265 if(!this.autoHideHd){
33266 var st = new Roo.util.DelayedTask(this.slideIn, this);
33267 this.autoHideHd = {
33268 "mouseout": function(e){
33269 if(!e.within(this.el, true)){
33273 "mouseover" : function(e){
33279 this.el.on(this.autoHideHd);
33283 clearAutoHide : function(){
33284 if(this.autoHide !== false){
33285 this.el.un("mouseout", this.autoHideHd.mouseout);
33286 this.el.un("mouseover", this.autoHideHd.mouseover);
33290 clearMonitor : function(){
33291 Roo.get(document).un("click", this.slideInIf, this);
33294 // these names are backwards but not changed for compat
33295 slideOut : function(){
33296 if(this.isSlid || this.el.hasActiveFx()){
33299 this.isSlid = true;
33300 if(this.collapseBtn){
33301 this.collapseBtn.hide();
33303 this.closeBtnState = this.closeBtn.getStyle('display');
33304 this.closeBtn.hide();
33306 this.stickBtn.show();
33309 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33310 this.beforeSlide();
33311 this.el.setStyle("z-index", 10001);
33312 this.el.slideIn(this.getSlideAnchor(), {
33313 callback: function(){
33315 this.initAutoHide();
33316 Roo.get(document).on("click", this.slideInIf, this);
33317 this.fireEvent("slideshow", this);
33324 afterSlideIn : function(){
33325 this.clearAutoHide();
33326 this.isSlid = false;
33327 this.clearMonitor();
33328 this.el.setStyle("z-index", "");
33329 if(this.collapseBtn){
33330 this.collapseBtn.show();
33332 this.closeBtn.setStyle('display', this.closeBtnState);
33334 this.stickBtn.hide();
33336 this.fireEvent("slidehide", this);
33339 slideIn : function(cb){
33340 if(!this.isSlid || this.el.hasActiveFx()){
33344 this.isSlid = false;
33345 this.beforeSlide();
33346 this.el.slideOut(this.getSlideAnchor(), {
33347 callback: function(){
33348 this.el.setLeftTop(-10000, -10000);
33350 this.afterSlideIn();
33358 slideInIf : function(e){
33359 if(!e.within(this.el)){
33364 animateCollapse : function(){
33365 this.beforeSlide();
33366 this.el.setStyle("z-index", 20000);
33367 var anchor = this.getSlideAnchor();
33368 this.el.slideOut(anchor, {
33369 callback : function(){
33370 this.el.setStyle("z-index", "");
33371 this.collapsedEl.slideIn(anchor, {duration:.3});
33373 this.el.setLocation(-10000,-10000);
33375 this.fireEvent("collapsed", this);
33382 animateExpand : function(){
33383 this.beforeSlide();
33384 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33385 this.el.setStyle("z-index", 20000);
33386 this.collapsedEl.hide({
33389 this.el.slideIn(this.getSlideAnchor(), {
33390 callback : function(){
33391 this.el.setStyle("z-index", "");
33394 this.split.el.show();
33396 this.fireEvent("invalidated", this);
33397 this.fireEvent("expanded", this);
33425 getAnchor : function(){
33426 return this.anchors[this.position];
33429 getCollapseAnchor : function(){
33430 return this.canchors[this.position];
33433 getSlideAnchor : function(){
33434 return this.sanchors[this.position];
33437 getAlignAdj : function(){
33438 var cm = this.cmargins;
33439 switch(this.position){
33455 getExpandAdj : function(){
33456 var c = this.collapsedEl, cm = this.cmargins;
33457 switch(this.position){
33459 return [-(cm.right+c.getWidth()+cm.left), 0];
33462 return [cm.right+c.getWidth()+cm.left, 0];
33465 return [0, -(cm.top+cm.bottom+c.getHeight())];
33468 return [0, cm.top+cm.bottom+c.getHeight()];
33474 * Ext JS Library 1.1.1
33475 * Copyright(c) 2006-2007, Ext JS, LLC.
33477 * Originally Released Under LGPL - original licence link has changed is not relivant.
33480 * <script type="text/javascript">
33483 * These classes are private internal classes
33485 Roo.bootstrap.layout.Center = function(config){
33486 config.region = "center";
33487 Roo.bootstrap.layout.Region.call(this, config);
33488 this.visible = true;
33489 this.minWidth = config.minWidth || 20;
33490 this.minHeight = config.minHeight || 20;
33493 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33495 // center panel can't be hidden
33499 // center panel can't be hidden
33502 getMinWidth: function(){
33503 return this.minWidth;
33506 getMinHeight: function(){
33507 return this.minHeight;
33520 Roo.bootstrap.layout.North = function(config)
33522 config.region = 'north';
33523 config.cursor = 'n-resize';
33525 Roo.bootstrap.layout.Split.call(this, config);
33529 this.split.placement = Roo.bootstrap.SplitBar.TOP;
33530 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33531 this.split.el.addClass("roo-layout-split-v");
33533 var size = config.initialSize || config.height;
33534 if(typeof size != "undefined"){
33535 this.el.setHeight(size);
33538 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33540 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33544 getBox : function(){
33545 if(this.collapsed){
33546 return this.collapsedEl.getBox();
33548 var box = this.el.getBox();
33550 box.height += this.split.el.getHeight();
33555 updateBox : function(box){
33556 if(this.split && !this.collapsed){
33557 box.height -= this.split.el.getHeight();
33558 this.split.el.setLeft(box.x);
33559 this.split.el.setTop(box.y+box.height);
33560 this.split.el.setWidth(box.width);
33562 if(this.collapsed){
33563 this.updateBody(box.width, null);
33565 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33573 Roo.bootstrap.layout.South = function(config){
33574 config.region = 'south';
33575 config.cursor = 's-resize';
33576 Roo.bootstrap.layout.Split.call(this, config);
33578 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33579 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33580 this.split.el.addClass("roo-layout-split-v");
33582 var size = config.initialSize || config.height;
33583 if(typeof size != "undefined"){
33584 this.el.setHeight(size);
33588 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33589 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33590 getBox : function(){
33591 if(this.collapsed){
33592 return this.collapsedEl.getBox();
33594 var box = this.el.getBox();
33596 var sh = this.split.el.getHeight();
33603 updateBox : function(box){
33604 if(this.split && !this.collapsed){
33605 var sh = this.split.el.getHeight();
33608 this.split.el.setLeft(box.x);
33609 this.split.el.setTop(box.y-sh);
33610 this.split.el.setWidth(box.width);
33612 if(this.collapsed){
33613 this.updateBody(box.width, null);
33615 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33619 Roo.bootstrap.layout.East = function(config){
33620 config.region = "east";
33621 config.cursor = "e-resize";
33622 Roo.bootstrap.layout.Split.call(this, config);
33624 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33625 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33626 this.split.el.addClass("roo-layout-split-h");
33628 var size = config.initialSize || config.width;
33629 if(typeof size != "undefined"){
33630 this.el.setWidth(size);
33633 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33634 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33635 getBox : function(){
33636 if(this.collapsed){
33637 return this.collapsedEl.getBox();
33639 var box = this.el.getBox();
33641 var sw = this.split.el.getWidth();
33648 updateBox : function(box){
33649 if(this.split && !this.collapsed){
33650 var sw = this.split.el.getWidth();
33652 this.split.el.setLeft(box.x);
33653 this.split.el.setTop(box.y);
33654 this.split.el.setHeight(box.height);
33657 if(this.collapsed){
33658 this.updateBody(null, box.height);
33660 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33664 Roo.bootstrap.layout.West = function(config){
33665 config.region = "west";
33666 config.cursor = "w-resize";
33668 Roo.bootstrap.layout.Split.call(this, config);
33670 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33671 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33672 this.split.el.addClass("roo-layout-split-h");
33676 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33677 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33679 onRender: function(ctr, pos)
33681 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33682 var size = this.config.initialSize || this.config.width;
33683 if(typeof size != "undefined"){
33684 this.el.setWidth(size);
33688 getBox : function(){
33689 if(this.collapsed){
33690 return this.collapsedEl.getBox();
33692 var box = this.el.getBox();
33694 box.width += this.split.el.getWidth();
33699 updateBox : function(box){
33700 if(this.split && !this.collapsed){
33701 var sw = this.split.el.getWidth();
33703 this.split.el.setLeft(box.x+box.width);
33704 this.split.el.setTop(box.y);
33705 this.split.el.setHeight(box.height);
33707 if(this.collapsed){
33708 this.updateBody(null, box.height);
33710 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33713 Roo.namespace("Roo.bootstrap.panel");/*
33715 * Ext JS Library 1.1.1
33716 * Copyright(c) 2006-2007, Ext JS, LLC.
33718 * Originally Released Under LGPL - original licence link has changed is not relivant.
33721 * <script type="text/javascript">
33724 * @class Roo.ContentPanel
33725 * @extends Roo.util.Observable
33726 * A basic ContentPanel element.
33727 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
33728 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
33729 * @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
33730 * @cfg {Boolean} closable True if the panel can be closed/removed
33731 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
33732 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33733 * @cfg {Toolbar} toolbar A toolbar for this panel
33734 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
33735 * @cfg {String} title The title for this panel
33736 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33737 * @cfg {String} url Calls {@link #setUrl} with this value
33738 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33739 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
33740 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
33741 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
33744 * Create a new ContentPanel.
33745 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33746 * @param {String/Object} config A string to set only the title or a config object
33747 * @param {String} content (optional) Set the HTML content for this panel
33748 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33750 Roo.bootstrap.panel.Content = function( config){
33752 var el = config.el;
33753 var content = config.content;
33755 if(config.autoCreate){ // xtype is available if this is called from factory
33758 this.el = Roo.get(el);
33759 if(!this.el && config && config.autoCreate){
33760 if(typeof config.autoCreate == "object"){
33761 if(!config.autoCreate.id){
33762 config.autoCreate.id = config.id||el;
33764 this.el = Roo.DomHelper.append(document.body,
33765 config.autoCreate, true);
33767 var elcfg = { tag: "div",
33768 cls: "roo-layout-inactive-content",
33772 elcfg.html = config.html;
33776 this.el = Roo.DomHelper.append(document.body, elcfg , true);
33779 this.closable = false;
33780 this.loaded = false;
33781 this.active = false;
33784 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33786 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33788 this.wrapEl = this.el.wrap();
33790 if (config.toolbar.items) {
33791 ti = config.toolbar.items ;
33792 delete config.toolbar.items ;
33796 this.toolbar.render(this.wrapEl, 'before');
33797 for(var i =0;i < ti.length;i++) {
33798 // Roo.log(['add child', items[i]]);
33799 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33801 this.toolbar.items = nitems;
33802 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33803 delete config.toolbar;
33807 // xtype created footer. - not sure if will work as we normally have to render first..
33808 if (this.footer && !this.footer.el && this.footer.xtype) {
33809 if (!this.wrapEl) {
33810 this.wrapEl = this.el.wrap();
33813 this.footer.container = this.wrapEl.createChild();
33815 this.footer = Roo.factory(this.footer, Roo);
33820 if(typeof config == "string"){
33821 this.title = config;
33823 Roo.apply(this, config);
33827 this.resizeEl = Roo.get(this.resizeEl, true);
33829 this.resizeEl = this.el;
33831 // handle view.xtype
33839 * Fires when this panel is activated.
33840 * @param {Roo.ContentPanel} this
33844 * @event deactivate
33845 * Fires when this panel is activated.
33846 * @param {Roo.ContentPanel} this
33848 "deactivate" : true,
33852 * Fires when this panel is resized if fitToFrame is true.
33853 * @param {Roo.ContentPanel} this
33854 * @param {Number} width The width after any component adjustments
33855 * @param {Number} height The height after any component adjustments
33861 * Fires when this tab is created
33862 * @param {Roo.ContentPanel} this
33873 if(this.autoScroll){
33874 this.resizeEl.setStyle("overflow", "auto");
33876 // fix randome scrolling
33877 //this.el.on('scroll', function() {
33878 // Roo.log('fix random scolling');
33879 // this.scrollTo('top',0);
33882 content = content || this.content;
33884 this.setContent(content);
33886 if(config && config.url){
33887 this.setUrl(this.url, this.params, this.loadOnce);
33892 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33894 if (this.view && typeof(this.view.xtype) != 'undefined') {
33895 this.view.el = this.el.appendChild(document.createElement("div"));
33896 this.view = Roo.factory(this.view);
33897 this.view.render && this.view.render(false, '');
33901 this.fireEvent('render', this);
33904 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33906 setRegion : function(region){
33907 this.region = region;
33909 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33911 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33916 * Returns the toolbar for this Panel if one was configured.
33917 * @return {Roo.Toolbar}
33919 getToolbar : function(){
33920 return this.toolbar;
33923 setActiveState : function(active){
33924 this.active = active;
33926 this.fireEvent("deactivate", this);
33928 this.fireEvent("activate", this);
33932 * Updates this panel's element
33933 * @param {String} content The new content
33934 * @param {Boolean} loadScripts (optional) true to look for and process scripts
33936 setContent : function(content, loadScripts){
33937 this.el.update(content, loadScripts);
33940 ignoreResize : function(w, h){
33941 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33944 this.lastSize = {width: w, height: h};
33949 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33950 * @return {Roo.UpdateManager} The UpdateManager
33952 getUpdateManager : function(){
33953 return this.el.getUpdateManager();
33956 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33957 * @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:
33960 url: "your-url.php",
33961 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33962 callback: yourFunction,
33963 scope: yourObject, //(optional scope)
33966 text: "Loading...",
33971 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33972 * 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.
33973 * @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}
33974 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33975 * @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.
33976 * @return {Roo.ContentPanel} this
33979 var um = this.el.getUpdateManager();
33980 um.update.apply(um, arguments);
33986 * 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.
33987 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33988 * @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)
33989 * @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)
33990 * @return {Roo.UpdateManager} The UpdateManager
33992 setUrl : function(url, params, loadOnce){
33993 if(this.refreshDelegate){
33994 this.removeListener("activate", this.refreshDelegate);
33996 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33997 this.on("activate", this.refreshDelegate);
33998 return this.el.getUpdateManager();
34001 _handleRefresh : function(url, params, loadOnce){
34002 if(!loadOnce || !this.loaded){
34003 var updater = this.el.getUpdateManager();
34004 updater.update(url, params, this._setLoaded.createDelegate(this));
34008 _setLoaded : function(){
34009 this.loaded = true;
34013 * Returns this panel's id
34016 getId : function(){
34021 * Returns this panel's element - used by regiosn to add.
34022 * @return {Roo.Element}
34024 getEl : function(){
34025 return this.wrapEl || this.el;
34030 adjustForComponents : function(width, height)
34032 //Roo.log('adjustForComponents ');
34033 if(this.resizeEl != this.el){
34034 width -= this.el.getFrameWidth('lr');
34035 height -= this.el.getFrameWidth('tb');
34038 var te = this.toolbar.getEl();
34039 height -= te.getHeight();
34040 te.setWidth(width);
34043 var te = this.footer.getEl();
34044 Roo.log("footer:" + te.getHeight());
34046 height -= te.getHeight();
34047 te.setWidth(width);
34051 if(this.adjustments){
34052 width += this.adjustments[0];
34053 height += this.adjustments[1];
34055 return {"width": width, "height": height};
34058 setSize : function(width, height){
34059 if(this.fitToFrame && !this.ignoreResize(width, height)){
34060 if(this.fitContainer && this.resizeEl != this.el){
34061 this.el.setSize(width, height);
34063 var size = this.adjustForComponents(width, height);
34064 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34065 this.fireEvent('resize', this, size.width, size.height);
34070 * Returns this panel's title
34073 getTitle : function(){
34078 * Set this panel's title
34079 * @param {String} title
34081 setTitle : function(title){
34082 this.title = title;
34084 this.region.updatePanelTitle(this, title);
34089 * Returns true is this panel was configured to be closable
34090 * @return {Boolean}
34092 isClosable : function(){
34093 return this.closable;
34096 beforeSlide : function(){
34098 this.resizeEl.clip();
34101 afterSlide : function(){
34103 this.resizeEl.unclip();
34107 * Force a content refresh from the URL specified in the {@link #setUrl} method.
34108 * Will fail silently if the {@link #setUrl} method has not been called.
34109 * This does not activate the panel, just updates its content.
34111 refresh : function(){
34112 if(this.refreshDelegate){
34113 this.loaded = false;
34114 this.refreshDelegate();
34119 * Destroys this panel
34121 destroy : function(){
34122 this.el.removeAllListeners();
34123 var tempEl = document.createElement("span");
34124 tempEl.appendChild(this.el.dom);
34125 tempEl.innerHTML = "";
34131 * form - if the content panel contains a form - this is a reference to it.
34132 * @type {Roo.form.Form}
34136 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34137 * This contains a reference to it.
34143 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34153 * @param {Object} cfg Xtype definition of item to add.
34157 getChildContainer: function () {
34158 return this.getEl();
34163 var ret = new Roo.factory(cfg);
34168 if (cfg.xtype.match(/^Form$/)) {
34171 //if (this.footer) {
34172 // el = this.footer.container.insertSibling(false, 'before');
34174 el = this.el.createChild();
34177 this.form = new Roo.form.Form(cfg);
34180 if ( this.form.allItems.length) {
34181 this.form.render(el.dom);
34185 // should only have one of theses..
34186 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34187 // views.. should not be just added - used named prop 'view''
34189 cfg.el = this.el.appendChild(document.createElement("div"));
34192 var ret = new Roo.factory(cfg);
34194 ret.render && ret.render(false, ''); // render blank..
34204 * @class Roo.bootstrap.panel.Grid
34205 * @extends Roo.bootstrap.panel.Content
34207 * Create a new GridPanel.
34208 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34209 * @param {Object} config A the config object
34215 Roo.bootstrap.panel.Grid = function(config)
34219 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34220 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34222 config.el = this.wrapper;
34223 //this.el = this.wrapper;
34225 if (config.container) {
34226 // ctor'ed from a Border/panel.grid
34229 this.wrapper.setStyle("overflow", "hidden");
34230 this.wrapper.addClass('roo-grid-container');
34235 if(config.toolbar){
34236 var tool_el = this.wrapper.createChild();
34237 this.toolbar = Roo.factory(config.toolbar);
34239 if (config.toolbar.items) {
34240 ti = config.toolbar.items ;
34241 delete config.toolbar.items ;
34245 this.toolbar.render(tool_el);
34246 for(var i =0;i < ti.length;i++) {
34247 // Roo.log(['add child', items[i]]);
34248 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34250 this.toolbar.items = nitems;
34252 delete config.toolbar;
34255 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34256 config.grid.scrollBody = true;;
34257 config.grid.monitorWindowResize = false; // turn off autosizing
34258 config.grid.autoHeight = false;
34259 config.grid.autoWidth = false;
34261 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34263 if (config.background) {
34264 // render grid on panel activation (if panel background)
34265 this.on('activate', function(gp) {
34266 if (!gp.grid.rendered) {
34267 gp.grid.render(el);
34268 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34274 this.grid.render(this.wrapper);
34275 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34278 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34279 // ??? needed ??? config.el = this.wrapper;
34284 // xtype created footer. - not sure if will work as we normally have to render first..
34285 if (this.footer && !this.footer.el && this.footer.xtype) {
34287 var ctr = this.grid.getView().getFooterPanel(true);
34288 this.footer.dataSource = this.grid.dataSource;
34289 this.footer = Roo.factory(this.footer, Roo);
34290 this.footer.render(ctr);
34300 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34301 getId : function(){
34302 return this.grid.id;
34306 * Returns the grid for this panel
34307 * @return {Roo.bootstrap.Table}
34309 getGrid : function(){
34313 setSize : function(width, height){
34314 if(!this.ignoreResize(width, height)){
34315 var grid = this.grid;
34316 var size = this.adjustForComponents(width, height);
34317 var gridel = grid.getGridEl();
34318 gridel.setSize(size.width, size.height);
34320 var thd = grid.getGridEl().select('thead',true).first();
34321 var tbd = grid.getGridEl().select('tbody', true).first();
34323 tbd.setSize(width, height - thd.getHeight());
34332 beforeSlide : function(){
34333 this.grid.getView().scroller.clip();
34336 afterSlide : function(){
34337 this.grid.getView().scroller.unclip();
34340 destroy : function(){
34341 this.grid.destroy();
34343 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
34348 * @class Roo.bootstrap.panel.Nest
34349 * @extends Roo.bootstrap.panel.Content
34351 * Create a new Panel, that can contain a layout.Border.
34354 * @param {Roo.BorderLayout} layout The layout for this panel
34355 * @param {String/Object} config A string to set only the title or a config object
34357 Roo.bootstrap.panel.Nest = function(config)
34359 // construct with only one argument..
34360 /* FIXME - implement nicer consturctors
34361 if (layout.layout) {
34363 layout = config.layout;
34364 delete config.layout;
34366 if (layout.xtype && !layout.getEl) {
34367 // then layout needs constructing..
34368 layout = Roo.factory(layout, Roo);
34372 config.el = config.layout.getEl();
34374 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34376 config.layout.monitorWindowResize = false; // turn off autosizing
34377 this.layout = config.layout;
34378 this.layout.getEl().addClass("roo-layout-nested-layout");
34385 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34387 setSize : function(width, height){
34388 if(!this.ignoreResize(width, height)){
34389 var size = this.adjustForComponents(width, height);
34390 var el = this.layout.getEl();
34391 el.setSize(size.width, size.height);
34392 var touch = el.dom.offsetWidth;
34393 this.layout.layout();
34394 // ie requires a double layout on the first pass
34395 if(Roo.isIE && !this.initialized){
34396 this.initialized = true;
34397 this.layout.layout();
34402 // activate all subpanels if not currently active..
34404 setActiveState : function(active){
34405 this.active = active;
34407 this.fireEvent("deactivate", this);
34411 this.fireEvent("activate", this);
34412 // not sure if this should happen before or after..
34413 if (!this.layout) {
34414 return; // should not happen..
34417 for (var r in this.layout.regions) {
34418 reg = this.layout.getRegion(r);
34419 if (reg.getActivePanel()) {
34420 //reg.showPanel(reg.getActivePanel()); // force it to activate..
34421 reg.setActivePanel(reg.getActivePanel());
34424 if (!reg.panels.length) {
34427 reg.showPanel(reg.getPanel(0));
34436 * Returns the nested BorderLayout for this panel
34437 * @return {Roo.BorderLayout}
34439 getLayout : function(){
34440 return this.layout;
34444 * Adds a xtype elements to the layout of the nested panel
34448 xtype : 'ContentPanel',
34455 xtype : 'NestedLayoutPanel',
34461 items : [ ... list of content panels or nested layout panels.. ]
34465 * @param {Object} cfg Xtype definition of item to add.
34467 addxtype : function(cfg) {
34468 return this.layout.addxtype(cfg);
34473 * Ext JS Library 1.1.1
34474 * Copyright(c) 2006-2007, Ext JS, LLC.
34476 * Originally Released Under LGPL - original licence link has changed is not relivant.
34479 * <script type="text/javascript">
34482 * @class Roo.TabPanel
34483 * @extends Roo.util.Observable
34484 * A lightweight tab container.
34488 // basic tabs 1, built from existing content
34489 var tabs = new Roo.TabPanel("tabs1");
34490 tabs.addTab("script", "View Script");
34491 tabs.addTab("markup", "View Markup");
34492 tabs.activate("script");
34494 // more advanced tabs, built from javascript
34495 var jtabs = new Roo.TabPanel("jtabs");
34496 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34498 // set up the UpdateManager
34499 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34500 var updater = tab2.getUpdateManager();
34501 updater.setDefaultUrl("ajax1.htm");
34502 tab2.on('activate', updater.refresh, updater, true);
34504 // Use setUrl for Ajax loading
34505 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34506 tab3.setUrl("ajax2.htm", null, true);
34509 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34512 jtabs.activate("jtabs-1");
34515 * Create a new TabPanel.
34516 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34517 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34519 Roo.bootstrap.panel.Tabs = function(config){
34521 * The container element for this TabPanel.
34522 * @type Roo.Element
34524 this.el = Roo.get(config.el);
34527 if(typeof config == "boolean"){
34528 this.tabPosition = config ? "bottom" : "top";
34530 Roo.apply(this, config);
34534 if(this.tabPosition == "bottom"){
34535 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34536 this.el.addClass("roo-tabs-bottom");
34538 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34539 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34540 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34542 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34544 if(this.tabPosition != "bottom"){
34545 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34546 * @type Roo.Element
34548 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34549 this.el.addClass("roo-tabs-top");
34553 this.bodyEl.setStyle("position", "relative");
34555 this.active = null;
34556 this.activateDelegate = this.activate.createDelegate(this);
34561 * Fires when the active tab changes
34562 * @param {Roo.TabPanel} this
34563 * @param {Roo.TabPanelItem} activePanel The new active tab
34567 * @event beforetabchange
34568 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34569 * @param {Roo.TabPanel} this
34570 * @param {Object} e Set cancel to true on this object to cancel the tab change
34571 * @param {Roo.TabPanelItem} tab The tab being changed to
34573 "beforetabchange" : true
34576 Roo.EventManager.onWindowResize(this.onResize, this);
34577 this.cpad = this.el.getPadding("lr");
34578 this.hiddenCount = 0;
34581 // toolbar on the tabbar support...
34582 if (this.toolbar) {
34583 alert("no toolbar support yet");
34584 this.toolbar = false;
34586 var tcfg = this.toolbar;
34587 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
34588 this.toolbar = new Roo.Toolbar(tcfg);
34589 if (Roo.isSafari) {
34590 var tbl = tcfg.container.child('table', true);
34591 tbl.setAttribute('width', '100%');
34599 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34602 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34604 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34606 tabPosition : "top",
34608 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34610 currentTabWidth : 0,
34612 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34616 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34620 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34622 preferredTabWidth : 175,
34624 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34626 resizeTabs : false,
34628 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34630 monitorResize : true,
34632 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
34637 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34638 * @param {String} id The id of the div to use <b>or create</b>
34639 * @param {String} text The text for the tab
34640 * @param {String} content (optional) Content to put in the TabPanelItem body
34641 * @param {Boolean} closable (optional) True to create a close icon on the tab
34642 * @return {Roo.TabPanelItem} The created TabPanelItem
34644 addTab : function(id, text, content, closable)
34646 var item = new Roo.bootstrap.panel.TabItem({
34650 closable : closable
34652 this.addTabItem(item);
34654 item.setContent(content);
34660 * Returns the {@link Roo.TabPanelItem} with the specified id/index
34661 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34662 * @return {Roo.TabPanelItem}
34664 getTab : function(id){
34665 return this.items[id];
34669 * Hides the {@link Roo.TabPanelItem} with the specified id/index
34670 * @param {String/Number} id The id or index of the TabPanelItem to hide.
34672 hideTab : function(id){
34673 var t = this.items[id];
34676 this.hiddenCount++;
34677 this.autoSizeTabs();
34682 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34683 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34685 unhideTab : function(id){
34686 var t = this.items[id];
34688 t.setHidden(false);
34689 this.hiddenCount--;
34690 this.autoSizeTabs();
34695 * Adds an existing {@link Roo.TabPanelItem}.
34696 * @param {Roo.TabPanelItem} item The TabPanelItem to add
34698 addTabItem : function(item){
34699 this.items[item.id] = item;
34700 this.items.push(item);
34701 // if(this.resizeTabs){
34702 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34703 // this.autoSizeTabs();
34705 // item.autoSize();
34710 * Removes a {@link Roo.TabPanelItem}.
34711 * @param {String/Number} id The id or index of the TabPanelItem to remove.
34713 removeTab : function(id){
34714 var items = this.items;
34715 var tab = items[id];
34716 if(!tab) { return; }
34717 var index = items.indexOf(tab);
34718 if(this.active == tab && items.length > 1){
34719 var newTab = this.getNextAvailable(index);
34724 this.stripEl.dom.removeChild(tab.pnode.dom);
34725 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34726 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34728 items.splice(index, 1);
34729 delete this.items[tab.id];
34730 tab.fireEvent("close", tab);
34731 tab.purgeListeners();
34732 this.autoSizeTabs();
34735 getNextAvailable : function(start){
34736 var items = this.items;
34738 // look for a next tab that will slide over to
34739 // replace the one being removed
34740 while(index < items.length){
34741 var item = items[++index];
34742 if(item && !item.isHidden()){
34746 // if one isn't found select the previous tab (on the left)
34749 var item = items[--index];
34750 if(item && !item.isHidden()){
34758 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34759 * @param {String/Number} id The id or index of the TabPanelItem to disable.
34761 disableTab : function(id){
34762 var tab = this.items[id];
34763 if(tab && this.active != tab){
34769 * Enables a {@link Roo.TabPanelItem} that is disabled.
34770 * @param {String/Number} id The id or index of the TabPanelItem to enable.
34772 enableTab : function(id){
34773 var tab = this.items[id];
34778 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34779 * @param {String/Number} id The id or index of the TabPanelItem to activate.
34780 * @return {Roo.TabPanelItem} The TabPanelItem.
34782 activate : function(id){
34783 var tab = this.items[id];
34787 if(tab == this.active || tab.disabled){
34791 this.fireEvent("beforetabchange", this, e, tab);
34792 if(e.cancel !== true && !tab.disabled){
34794 this.active.hide();
34796 this.active = this.items[id];
34797 this.active.show();
34798 this.fireEvent("tabchange", this, this.active);
34804 * Gets the active {@link Roo.TabPanelItem}.
34805 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34807 getActiveTab : function(){
34808 return this.active;
34812 * Updates the tab body element to fit the height of the container element
34813 * for overflow scrolling
34814 * @param {Number} targetHeight (optional) Override the starting height from the elements height
34816 syncHeight : function(targetHeight){
34817 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34818 var bm = this.bodyEl.getMargins();
34819 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34820 this.bodyEl.setHeight(newHeight);
34824 onResize : function(){
34825 if(this.monitorResize){
34826 this.autoSizeTabs();
34831 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34833 beginUpdate : function(){
34834 this.updating = true;
34838 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34840 endUpdate : function(){
34841 this.updating = false;
34842 this.autoSizeTabs();
34846 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34848 autoSizeTabs : function(){
34849 var count = this.items.length;
34850 var vcount = count - this.hiddenCount;
34851 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34854 var w = Math.max(this.el.getWidth() - this.cpad, 10);
34855 var availWidth = Math.floor(w / vcount);
34856 var b = this.stripBody;
34857 if(b.getWidth() > w){
34858 var tabs = this.items;
34859 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34860 if(availWidth < this.minTabWidth){
34861 /*if(!this.sleft){ // incomplete scrolling code
34862 this.createScrollButtons();
34865 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34868 if(this.currentTabWidth < this.preferredTabWidth){
34869 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34875 * Returns the number of tabs in this TabPanel.
34878 getCount : function(){
34879 return this.items.length;
34883 * Resizes all the tabs to the passed width
34884 * @param {Number} The new width
34886 setTabWidth : function(width){
34887 this.currentTabWidth = width;
34888 for(var i = 0, len = this.items.length; i < len; i++) {
34889 if(!this.items[i].isHidden()) {
34890 this.items[i].setWidth(width);
34896 * Destroys this TabPanel
34897 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34899 destroy : function(removeEl){
34900 Roo.EventManager.removeResizeListener(this.onResize, this);
34901 for(var i = 0, len = this.items.length; i < len; i++){
34902 this.items[i].purgeListeners();
34904 if(removeEl === true){
34905 this.el.update("");
34910 createStrip : function(container)
34912 var strip = document.createElement("nav");
34913 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34914 container.appendChild(strip);
34918 createStripList : function(strip)
34920 // div wrapper for retard IE
34921 // returns the "tr" element.
34922 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34923 //'<div class="x-tabs-strip-wrap">'+
34924 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34925 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34926 return strip.firstChild; //.firstChild.firstChild.firstChild;
34928 createBody : function(container)
34930 var body = document.createElement("div");
34931 Roo.id(body, "tab-body");
34932 //Roo.fly(body).addClass("x-tabs-body");
34933 Roo.fly(body).addClass("tab-content");
34934 container.appendChild(body);
34937 createItemBody :function(bodyEl, id){
34938 var body = Roo.getDom(id);
34940 body = document.createElement("div");
34943 //Roo.fly(body).addClass("x-tabs-item-body");
34944 Roo.fly(body).addClass("tab-pane");
34945 bodyEl.insertBefore(body, bodyEl.firstChild);
34949 createStripElements : function(stripEl, text, closable)
34951 var td = document.createElement("li"); // was td..
34952 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34953 //stripEl.appendChild(td);
34955 td.className = "x-tabs-closable";
34956 if(!this.closeTpl){
34957 this.closeTpl = new Roo.Template(
34958 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34959 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34960 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
34963 var el = this.closeTpl.overwrite(td, {"text": text});
34964 var close = el.getElementsByTagName("div")[0];
34965 var inner = el.getElementsByTagName("em")[0];
34966 return {"el": el, "close": close, "inner": inner};
34969 // not sure what this is..
34971 //this.tabTpl = new Roo.Template(
34972 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34973 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34975 this.tabTpl = new Roo.Template(
34977 '<span unselectable="on"' +
34978 (this.disableTooltips ? '' : ' title="{text}"') +
34979 ' >{text}</span></span></a>'
34983 var el = this.tabTpl.overwrite(td, {"text": text});
34984 var inner = el.getElementsByTagName("span")[0];
34985 return {"el": el, "inner": inner};
34993 * @class Roo.TabPanelItem
34994 * @extends Roo.util.Observable
34995 * Represents an individual item (tab plus body) in a TabPanel.
34996 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34997 * @param {String} id The id of this TabPanelItem
34998 * @param {String} text The text for the tab of this TabPanelItem
34999 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35001 Roo.bootstrap.panel.TabItem = function(config){
35003 * The {@link Roo.TabPanel} this TabPanelItem belongs to
35004 * @type Roo.TabPanel
35006 this.tabPanel = config.panel;
35008 * The id for this TabPanelItem
35011 this.id = config.id;
35013 this.disabled = false;
35015 this.text = config.text;
35017 this.loaded = false;
35018 this.closable = config.closable;
35021 * The body element for this TabPanelItem.
35022 * @type Roo.Element
35024 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35025 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35026 this.bodyEl.setStyle("display", "block");
35027 this.bodyEl.setStyle("zoom", "1");
35028 //this.hideAction();
35030 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35032 this.el = Roo.get(els.el);
35033 this.inner = Roo.get(els.inner, true);
35034 this.textEl = Roo.get(this.el.dom.firstChild, true);
35035 this.pnode = Roo.get(els.el.parentNode, true);
35036 this.el.on("mousedown", this.onTabMouseDown, this);
35037 this.el.on("click", this.onTabClick, this);
35039 if(config.closable){
35040 var c = Roo.get(els.close, true);
35041 c.dom.title = this.closeText;
35042 c.addClassOnOver("close-over");
35043 c.on("click", this.closeClick, this);
35049 * Fires when this tab becomes the active tab.
35050 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35051 * @param {Roo.TabPanelItem} this
35055 * @event beforeclose
35056 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35057 * @param {Roo.TabPanelItem} this
35058 * @param {Object} e Set cancel to true on this object to cancel the close.
35060 "beforeclose": true,
35063 * Fires when this tab is closed.
35064 * @param {Roo.TabPanelItem} this
35068 * @event deactivate
35069 * Fires when this tab is no longer the active tab.
35070 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35071 * @param {Roo.TabPanelItem} this
35073 "deactivate" : true
35075 this.hidden = false;
35077 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35080 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35082 purgeListeners : function(){
35083 Roo.util.Observable.prototype.purgeListeners.call(this);
35084 this.el.removeAllListeners();
35087 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35090 this.pnode.addClass("active");
35093 this.tabPanel.stripWrap.repaint();
35095 this.fireEvent("activate", this.tabPanel, this);
35099 * Returns true if this tab is the active tab.
35100 * @return {Boolean}
35102 isActive : function(){
35103 return this.tabPanel.getActiveTab() == this;
35107 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35110 this.pnode.removeClass("active");
35112 this.fireEvent("deactivate", this.tabPanel, this);
35115 hideAction : function(){
35116 this.bodyEl.hide();
35117 this.bodyEl.setStyle("position", "absolute");
35118 this.bodyEl.setLeft("-20000px");
35119 this.bodyEl.setTop("-20000px");
35122 showAction : function(){
35123 this.bodyEl.setStyle("position", "relative");
35124 this.bodyEl.setTop("");
35125 this.bodyEl.setLeft("");
35126 this.bodyEl.show();
35130 * Set the tooltip for the tab.
35131 * @param {String} tooltip The tab's tooltip
35133 setTooltip : function(text){
35134 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35135 this.textEl.dom.qtip = text;
35136 this.textEl.dom.removeAttribute('title');
35138 this.textEl.dom.title = text;
35142 onTabClick : function(e){
35143 e.preventDefault();
35144 this.tabPanel.activate(this.id);
35147 onTabMouseDown : function(e){
35148 e.preventDefault();
35149 this.tabPanel.activate(this.id);
35152 getWidth : function(){
35153 return this.inner.getWidth();
35156 setWidth : function(width){
35157 var iwidth = width - this.pnode.getPadding("lr");
35158 this.inner.setWidth(iwidth);
35159 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35160 this.pnode.setWidth(width);
35164 * Show or hide the tab
35165 * @param {Boolean} hidden True to hide or false to show.
35167 setHidden : function(hidden){
35168 this.hidden = hidden;
35169 this.pnode.setStyle("display", hidden ? "none" : "");
35173 * Returns true if this tab is "hidden"
35174 * @return {Boolean}
35176 isHidden : function(){
35177 return this.hidden;
35181 * Returns the text for this tab
35184 getText : function(){
35188 autoSize : function(){
35189 //this.el.beginMeasure();
35190 this.textEl.setWidth(1);
35192 * #2804 [new] Tabs in Roojs
35193 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35195 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35196 //this.el.endMeasure();
35200 * Sets the text for the tab (Note: this also sets the tooltip text)
35201 * @param {String} text The tab's text and tooltip
35203 setText : function(text){
35205 this.textEl.update(text);
35206 this.setTooltip(text);
35207 //if(!this.tabPanel.resizeTabs){
35208 // this.autoSize();
35212 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35214 activate : function(){
35215 this.tabPanel.activate(this.id);
35219 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35221 disable : function(){
35222 if(this.tabPanel.active != this){
35223 this.disabled = true;
35224 this.pnode.addClass("disabled");
35229 * Enables this TabPanelItem if it was previously disabled.
35231 enable : function(){
35232 this.disabled = false;
35233 this.pnode.removeClass("disabled");
35237 * Sets the content for this TabPanelItem.
35238 * @param {String} content The content
35239 * @param {Boolean} loadScripts true to look for and load scripts
35241 setContent : function(content, loadScripts){
35242 this.bodyEl.update(content, loadScripts);
35246 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35247 * @return {Roo.UpdateManager} The UpdateManager
35249 getUpdateManager : function(){
35250 return this.bodyEl.getUpdateManager();
35254 * Set a URL to be used to load the content for this TabPanelItem.
35255 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35256 * @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)
35257 * @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)
35258 * @return {Roo.UpdateManager} The UpdateManager
35260 setUrl : function(url, params, loadOnce){
35261 if(this.refreshDelegate){
35262 this.un('activate', this.refreshDelegate);
35264 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35265 this.on("activate", this.refreshDelegate);
35266 return this.bodyEl.getUpdateManager();
35270 _handleRefresh : function(url, params, loadOnce){
35271 if(!loadOnce || !this.loaded){
35272 var updater = this.bodyEl.getUpdateManager();
35273 updater.update(url, params, this._setLoaded.createDelegate(this));
35278 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
35279 * Will fail silently if the setUrl method has not been called.
35280 * This does not activate the panel, just updates its content.
35282 refresh : function(){
35283 if(this.refreshDelegate){
35284 this.loaded = false;
35285 this.refreshDelegate();
35290 _setLoaded : function(){
35291 this.loaded = true;
35295 closeClick : function(e){
35298 this.fireEvent("beforeclose", this, o);
35299 if(o.cancel !== true){
35300 this.tabPanel.removeTab(this.id);
35304 * The text displayed in the tooltip for the close icon.
35307 closeText : "Close this tab"