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 this.selModel.initEvents();
5978 //Roo.log('initEvents with ds!!!!');
5980 this.mainBody = this.el.select('tbody', true).first();
5981 this.mainHead = this.el.select('thead', true).first();
5988 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5989 e.on('click', _this.sort, _this);
5992 this.el.on("click", this.onClick, this);
5993 this.el.on("dblclick", this.onDblClick, this);
5995 // why is this done????? = it breaks dialogs??
5996 //this.parent().el.setStyle('position', 'relative');
6000 this.footer.parentId = this.id;
6001 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6004 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6006 this.store.on('load', this.onLoad, this);
6007 this.store.on('beforeload', this.onBeforeLoad, this);
6008 this.store.on('update', this.onUpdate, this);
6009 this.store.on('add', this.onAdd, this);
6010 this.store.on("clear", this.clear, this);
6012 this.el.on("contextmenu", this.onContextMenu, this);
6014 this.mainBody.on('scroll', this.onBodyScroll, this);
6019 onContextMenu : function(e, t)
6021 this.processEvent("contextmenu", e);
6024 processEvent : function(name, e)
6026 if (name != 'touchstart' ) {
6027 this.fireEvent(name, e);
6030 var t = e.getTarget();
6032 var cell = Roo.get(t);
6038 if(cell.findParent('tfoot', false, true)){
6042 if(cell.findParent('thead', false, true)){
6044 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6045 cell = Roo.get(t).findParent('th', false, true);
6047 Roo.log("failed to find th in thead?");
6048 Roo.log(e.getTarget());
6053 var cellIndex = cell.dom.cellIndex;
6055 var ename = name == 'touchstart' ? 'click' : name;
6056 this.fireEvent("header" + ename, this, cellIndex, e);
6061 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6062 cell = Roo.get(t).findParent('td', false, true);
6064 Roo.log("failed to find th in tbody?");
6065 Roo.log(e.getTarget());
6070 var row = cell.findParent('tr', false, true);
6071 var cellIndex = cell.dom.cellIndex;
6072 var rowIndex = row.dom.rowIndex - 1;
6076 this.fireEvent("row" + name, this, rowIndex, e);
6080 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6086 onMouseover : function(e, el)
6088 var cell = Roo.get(el);
6094 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6095 cell = cell.findParent('td', false, true);
6098 var row = cell.findParent('tr', false, true);
6099 var cellIndex = cell.dom.cellIndex;
6100 var rowIndex = row.dom.rowIndex - 1; // start from 0
6102 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6106 onMouseout : function(e, el)
6108 var cell = Roo.get(el);
6114 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6115 cell = cell.findParent('td', false, true);
6118 var row = cell.findParent('tr', false, true);
6119 var cellIndex = cell.dom.cellIndex;
6120 var rowIndex = row.dom.rowIndex - 1; // start from 0
6122 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6126 onClick : function(e, el)
6128 var cell = Roo.get(el);
6130 if(!cell || (!this.cellSelection && !this.rowSelection)){
6134 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6135 cell = cell.findParent('td', false, true);
6138 if(!cell || typeof(cell) == 'undefined'){
6142 var row = cell.findParent('tr', false, true);
6144 if(!row || typeof(row) == 'undefined'){
6148 var cellIndex = cell.dom.cellIndex;
6149 var rowIndex = this.getRowIndex(row);
6151 // why??? - should these not be based on SelectionModel?
6152 if(this.cellSelection){
6153 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6156 if(this.rowSelection){
6157 this.fireEvent('rowclick', this, row, rowIndex, e);
6163 onDblClick : function(e,el)
6165 var cell = Roo.get(el);
6167 if(!cell || (!this.CellSelection && !this.RowSelection)){
6171 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6172 cell = cell.findParent('td', false, true);
6175 if(!cell || typeof(cell) == 'undefined'){
6179 var row = cell.findParent('tr', false, true);
6181 if(!row || typeof(row) == 'undefined'){
6185 var cellIndex = cell.dom.cellIndex;
6186 var rowIndex = this.getRowIndex(row);
6188 if(this.CellSelection){
6189 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6192 if(this.RowSelection){
6193 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6197 sort : function(e,el)
6199 var col = Roo.get(el);
6201 if(!col.hasClass('sortable')){
6205 var sort = col.attr('sort');
6208 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6212 this.store.sortInfo = {field : sort, direction : dir};
6215 Roo.log("calling footer first");
6216 this.footer.onClick('first');
6219 this.store.load({ params : { start : 0 } });
6223 renderHeader : function()
6231 this.totalWidth = 0;
6233 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6235 var config = cm.config[i];
6240 html: cm.getColumnHeader(i)
6245 if(typeof(config.sortable) != 'undefined' && config.sortable){
6247 c.html = '<i class="glyphicon"></i>' + c.html;
6250 if(typeof(config.lgHeader) != 'undefined'){
6251 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6254 if(typeof(config.mdHeader) != 'undefined'){
6255 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6258 if(typeof(config.smHeader) != 'undefined'){
6259 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6262 if(typeof(config.xsHeader) != 'undefined'){
6263 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6270 if(typeof(config.tooltip) != 'undefined'){
6271 c.tooltip = config.tooltip;
6274 if(typeof(config.colspan) != 'undefined'){
6275 c.colspan = config.colspan;
6278 if(typeof(config.hidden) != 'undefined' && config.hidden){
6279 c.style += ' display:none;';
6282 if(typeof(config.dataIndex) != 'undefined'){
6283 c.sort = config.dataIndex;
6288 if(typeof(config.align) != 'undefined' && config.align.length){
6289 c.style += ' text-align:' + config.align + ';';
6292 if(typeof(config.width) != 'undefined'){
6293 c.style += ' width:' + config.width + 'px;';
6294 this.totalWidth += config.width;
6296 this.totalWidth += 100; // assume minimum of 100 per column?
6299 if(typeof(config.cls) != 'undefined'){
6300 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6303 ['xs','sm','md','lg'].map(function(size){
6305 if(typeof(config[size]) == 'undefined'){
6309 if (!config[size]) { // 0 = hidden
6310 c.cls += ' hidden-' + size;
6314 c.cls += ' col-' + size + '-' + config[size];
6324 renderBody : function()
6334 colspan : this.cm.getColumnCount()
6344 renderFooter : function()
6354 colspan : this.cm.getColumnCount()
6368 // Roo.log('ds onload');
6373 var ds = this.store;
6375 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6376 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6377 if (_this.store.sortInfo) {
6379 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6380 e.select('i', true).addClass(['glyphicon-arrow-up']);
6383 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6384 e.select('i', true).addClass(['glyphicon-arrow-down']);
6389 var tbody = this.mainBody;
6391 if(ds.getCount() > 0){
6392 ds.data.each(function(d,rowIndex){
6393 var row = this.renderRow(cm, ds, rowIndex);
6395 tbody.createChild(row);
6399 if(row.cellObjects.length){
6400 Roo.each(row.cellObjects, function(r){
6401 _this.renderCellObject(r);
6408 Roo.each(this.el.select('tbody td', true).elements, function(e){
6409 e.on('mouseover', _this.onMouseover, _this);
6412 Roo.each(this.el.select('tbody td', true).elements, function(e){
6413 e.on('mouseout', _this.onMouseout, _this);
6415 this.fireEvent('rowsrendered', this);
6416 //if(this.loadMask){
6417 // this.maskEl.hide();
6424 onUpdate : function(ds,record)
6426 this.refreshRow(record);
6429 onRemove : function(ds, record, index, isUpdate){
6430 if(isUpdate !== true){
6431 this.fireEvent("beforerowremoved", this, index, record);
6433 var bt = this.mainBody.dom;
6435 var rows = this.el.select('tbody > tr', true).elements;
6437 if(typeof(rows[index]) != 'undefined'){
6438 bt.removeChild(rows[index].dom);
6441 // if(bt.rows[index]){
6442 // bt.removeChild(bt.rows[index]);
6445 if(isUpdate !== true){
6446 //this.stripeRows(index);
6447 //this.syncRowHeights(index, index);
6449 this.fireEvent("rowremoved", this, index, record);
6453 onAdd : function(ds, records, rowIndex)
6455 //Roo.log('on Add called');
6456 // - note this does not handle multiple adding very well..
6457 var bt = this.mainBody.dom;
6458 for (var i =0 ; i < records.length;i++) {
6459 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6460 //Roo.log(records[i]);
6461 //Roo.log(this.store.getAt(rowIndex+i));
6462 this.insertRow(this.store, rowIndex + i, false);
6469 refreshRow : function(record){
6470 var ds = this.store, index;
6471 if(typeof record == 'number'){
6473 record = ds.getAt(index);
6475 index = ds.indexOf(record);
6477 this.insertRow(ds, index, true);
6478 this.onRemove(ds, record, index+1, true);
6479 //this.syncRowHeights(index, index);
6481 this.fireEvent("rowupdated", this, index, record);
6484 insertRow : function(dm, rowIndex, isUpdate){
6487 this.fireEvent("beforerowsinserted", this, rowIndex);
6489 //var s = this.getScrollState();
6490 var row = this.renderRow(this.cm, this.store, rowIndex);
6491 // insert before rowIndex..
6492 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6496 if(row.cellObjects.length){
6497 Roo.each(row.cellObjects, function(r){
6498 _this.renderCellObject(r);
6503 this.fireEvent("rowsinserted", this, rowIndex);
6504 //this.syncRowHeights(firstRow, lastRow);
6505 //this.stripeRows(firstRow);
6512 getRowDom : function(rowIndex)
6514 var rows = this.el.select('tbody > tr', true).elements;
6516 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6519 // returns the object tree for a tr..
6522 renderRow : function(cm, ds, rowIndex)
6525 var d = ds.getAt(rowIndex);
6532 var cellObjects = [];
6534 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6535 var config = cm.config[i];
6537 var renderer = cm.getRenderer(i);
6541 if(typeof(renderer) !== 'undefined'){
6542 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6544 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6545 // and are rendered into the cells after the row is rendered - using the id for the element.
6547 if(typeof(value) === 'object'){
6557 rowIndex : rowIndex,
6562 this.fireEvent('rowclass', this, rowcfg);
6566 cls : rowcfg.rowClass,
6568 html: (typeof(value) === 'object') ? '' : value
6575 if(typeof(config.colspan) != 'undefined'){
6576 td.colspan = config.colspan;
6579 if(typeof(config.hidden) != 'undefined' && config.hidden){
6580 td.style += ' display:none;';
6583 if(typeof(config.align) != 'undefined' && config.align.length){
6584 td.style += ' text-align:' + config.align + ';';
6587 if(typeof(config.width) != 'undefined'){
6588 td.style += ' width:' + config.width + 'px;';
6591 if(typeof(config.cursor) != 'undefined'){
6592 td.style += ' cursor:' + config.cursor + ';';
6595 if(typeof(config.cls) != 'undefined'){
6596 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6599 ['xs','sm','md','lg'].map(function(size){
6601 if(typeof(config[size]) == 'undefined'){
6605 if (!config[size]) { // 0 = hidden
6606 td.cls += ' hidden-' + size;
6610 td.cls += ' col-' + size + '-' + config[size];
6618 row.cellObjects = cellObjects;
6626 onBeforeLoad : function()
6628 //Roo.log('ds onBeforeLoad');
6632 //if(this.loadMask){
6633 // this.maskEl.show();
6641 this.el.select('tbody', true).first().dom.innerHTML = '';
6644 * Show or hide a row.
6645 * @param {Number} rowIndex to show or hide
6646 * @param {Boolean} state hide
6648 setRowVisibility : function(rowIndex, state)
6650 var bt = this.mainBody.dom;
6652 var rows = this.el.select('tbody > tr', true).elements;
6654 if(typeof(rows[rowIndex]) == 'undefined'){
6657 rows[rowIndex].dom.style.display = state ? '' : 'none';
6661 getSelectionModel : function(){
6663 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6665 return this.selModel;
6668 * Render the Roo.bootstrap object from renderder
6670 renderCellObject : function(r)
6674 var t = r.cfg.render(r.container);
6677 Roo.each(r.cfg.cn, function(c){
6679 container: t.getChildContainer(),
6682 _this.renderCellObject(child);
6687 getRowIndex : function(row)
6691 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6702 * Returns the grid's underlying element = used by panel.Grid
6703 * @return {Element} The element
6705 getGridEl : function(){
6709 * Forces a resize - used by panel.Grid
6710 * @return {Element} The element
6712 autoSize : function()
6714 //var ctr = Roo.get(this.container.dom.parentElement);
6715 var ctr = Roo.get(this.el.dom);
6717 var thd = this.getGridEl().select('thead',true).first();
6718 var tbd = this.getGridEl().select('tbody', true).first();
6721 var cw = ctr.getWidth();
6725 tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6726 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6729 cw = Math.max(cw, this.totalWidth);
6730 this.getGridEl().select('tr',true).setWidth(cw);
6731 // resize 'expandable coloumn?
6733 return; // we doe not have a view in this design..
6736 onBodyScroll: function()
6739 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6740 this.mainHead.setStyle({
6741 'position' : 'relative',
6742 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6759 * @class Roo.bootstrap.TableCell
6760 * @extends Roo.bootstrap.Component
6761 * Bootstrap TableCell class
6762 * @cfg {String} html cell contain text
6763 * @cfg {String} cls cell class
6764 * @cfg {String} tag cell tag (td|th) default td
6765 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6766 * @cfg {String} align Aligns the content in a cell
6767 * @cfg {String} axis Categorizes cells
6768 * @cfg {String} bgcolor Specifies the background color of a cell
6769 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6770 * @cfg {Number} colspan Specifies the number of columns a cell should span
6771 * @cfg {String} headers Specifies one or more header cells a cell is related to
6772 * @cfg {Number} height Sets the height of a cell
6773 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6774 * @cfg {Number} rowspan Sets the number of rows a cell should span
6775 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6776 * @cfg {String} valign Vertical aligns the content in a cell
6777 * @cfg {Number} width Specifies the width of a cell
6780 * Create a new TableCell
6781 * @param {Object} config The config object
6784 Roo.bootstrap.TableCell = function(config){
6785 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6788 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6808 getAutoCreate : function(){
6809 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6829 cfg.align=this.align
6835 cfg.bgcolor=this.bgcolor
6838 cfg.charoff=this.charoff
6841 cfg.colspan=this.colspan
6844 cfg.headers=this.headers
6847 cfg.height=this.height
6850 cfg.nowrap=this.nowrap
6853 cfg.rowspan=this.rowspan
6856 cfg.scope=this.scope
6859 cfg.valign=this.valign
6862 cfg.width=this.width
6881 * @class Roo.bootstrap.TableRow
6882 * @extends Roo.bootstrap.Component
6883 * Bootstrap TableRow class
6884 * @cfg {String} cls row class
6885 * @cfg {String} align Aligns the content in a table row
6886 * @cfg {String} bgcolor Specifies a background color for a table row
6887 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6888 * @cfg {String} valign Vertical aligns the content in a table row
6891 * Create a new TableRow
6892 * @param {Object} config The config object
6895 Roo.bootstrap.TableRow = function(config){
6896 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6899 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6907 getAutoCreate : function(){
6908 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6918 cfg.align = this.align;
6921 cfg.bgcolor = this.bgcolor;
6924 cfg.charoff = this.charoff;
6927 cfg.valign = this.valign;
6945 * @class Roo.bootstrap.TableBody
6946 * @extends Roo.bootstrap.Component
6947 * Bootstrap TableBody class
6948 * @cfg {String} cls element class
6949 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6950 * @cfg {String} align Aligns the content inside the element
6951 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6952 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6955 * Create a new TableBody
6956 * @param {Object} config The config object
6959 Roo.bootstrap.TableBody = function(config){
6960 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6963 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6971 getAutoCreate : function(){
6972 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6986 cfg.align = this.align;
6989 cfg.charoff = this.charoff;
6992 cfg.valign = this.valign;
6999 // initEvents : function()
7006 // this.store = Roo.factory(this.store, Roo.data);
7007 // this.store.on('load', this.onLoad, this);
7009 // this.store.load();
7013 // onLoad: function ()
7015 // this.fireEvent('load', this);
7025 * Ext JS Library 1.1.1
7026 * Copyright(c) 2006-2007, Ext JS, LLC.
7028 * Originally Released Under LGPL - original licence link has changed is not relivant.
7031 * <script type="text/javascript">
7034 // as we use this in bootstrap.
7035 Roo.namespace('Roo.form');
7037 * @class Roo.form.Action
7038 * Internal Class used to handle form actions
7040 * @param {Roo.form.BasicForm} el The form element or its id
7041 * @param {Object} config Configuration options
7046 // define the action interface
7047 Roo.form.Action = function(form, options){
7049 this.options = options || {};
7052 * Client Validation Failed
7055 Roo.form.Action.CLIENT_INVALID = 'client';
7057 * Server Validation Failed
7060 Roo.form.Action.SERVER_INVALID = 'server';
7062 * Connect to Server Failed
7065 Roo.form.Action.CONNECT_FAILURE = 'connect';
7067 * Reading Data from Server Failed
7070 Roo.form.Action.LOAD_FAILURE = 'load';
7072 Roo.form.Action.prototype = {
7074 failureType : undefined,
7075 response : undefined,
7079 run : function(options){
7084 success : function(response){
7089 handleResponse : function(response){
7093 // default connection failure
7094 failure : function(response){
7096 this.response = response;
7097 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7098 this.form.afterAction(this, false);
7101 processResponse : function(response){
7102 this.response = response;
7103 if(!response.responseText){
7106 this.result = this.handleResponse(response);
7110 // utility functions used internally
7111 getUrl : function(appendParams){
7112 var url = this.options.url || this.form.url || this.form.el.dom.action;
7114 var p = this.getParams();
7116 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7122 getMethod : function(){
7123 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7126 getParams : function(){
7127 var bp = this.form.baseParams;
7128 var p = this.options.params;
7130 if(typeof p == "object"){
7131 p = Roo.urlEncode(Roo.applyIf(p, bp));
7132 }else if(typeof p == 'string' && bp){
7133 p += '&' + Roo.urlEncode(bp);
7136 p = Roo.urlEncode(bp);
7141 createCallback : function(){
7143 success: this.success,
7144 failure: this.failure,
7146 timeout: (this.form.timeout*1000),
7147 upload: this.form.fileUpload ? this.success : undefined
7152 Roo.form.Action.Submit = function(form, options){
7153 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7156 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7159 haveProgress : false,
7160 uploadComplete : false,
7162 // uploadProgress indicator.
7163 uploadProgress : function()
7165 if (!this.form.progressUrl) {
7169 if (!this.haveProgress) {
7170 Roo.MessageBox.progress("Uploading", "Uploading");
7172 if (this.uploadComplete) {
7173 Roo.MessageBox.hide();
7177 this.haveProgress = true;
7179 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7181 var c = new Roo.data.Connection();
7183 url : this.form.progressUrl,
7188 success : function(req){
7189 //console.log(data);
7193 rdata = Roo.decode(req.responseText)
7195 Roo.log("Invalid data from server..");
7199 if (!rdata || !rdata.success) {
7201 Roo.MessageBox.alert(Roo.encode(rdata));
7204 var data = rdata.data;
7206 if (this.uploadComplete) {
7207 Roo.MessageBox.hide();
7212 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7213 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7216 this.uploadProgress.defer(2000,this);
7219 failure: function(data) {
7220 Roo.log('progress url failed ');
7231 // run get Values on the form, so it syncs any secondary forms.
7232 this.form.getValues();
7234 var o = this.options;
7235 var method = this.getMethod();
7236 var isPost = method == 'POST';
7237 if(o.clientValidation === false || this.form.isValid()){
7239 if (this.form.progressUrl) {
7240 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7241 (new Date() * 1) + '' + Math.random());
7246 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7247 form:this.form.el.dom,
7248 url:this.getUrl(!isPost),
7250 params:isPost ? this.getParams() : null,
7251 isUpload: this.form.fileUpload
7254 this.uploadProgress();
7256 }else if (o.clientValidation !== false){ // client validation failed
7257 this.failureType = Roo.form.Action.CLIENT_INVALID;
7258 this.form.afterAction(this, false);
7262 success : function(response)
7264 this.uploadComplete= true;
7265 if (this.haveProgress) {
7266 Roo.MessageBox.hide();
7270 var result = this.processResponse(response);
7271 if(result === true || result.success){
7272 this.form.afterAction(this, true);
7276 this.form.markInvalid(result.errors);
7277 this.failureType = Roo.form.Action.SERVER_INVALID;
7279 this.form.afterAction(this, false);
7281 failure : function(response)
7283 this.uploadComplete= true;
7284 if (this.haveProgress) {
7285 Roo.MessageBox.hide();
7288 this.response = response;
7289 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7290 this.form.afterAction(this, false);
7293 handleResponse : function(response){
7294 if(this.form.errorReader){
7295 var rs = this.form.errorReader.read(response);
7298 for(var i = 0, len = rs.records.length; i < len; i++) {
7299 var r = rs.records[i];
7303 if(errors.length < 1){
7307 success : rs.success,
7313 ret = Roo.decode(response.responseText);
7317 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7327 Roo.form.Action.Load = function(form, options){
7328 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7329 this.reader = this.form.reader;
7332 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7337 Roo.Ajax.request(Roo.apply(
7338 this.createCallback(), {
7339 method:this.getMethod(),
7340 url:this.getUrl(false),
7341 params:this.getParams()
7345 success : function(response){
7347 var result = this.processResponse(response);
7348 if(result === true || !result.success || !result.data){
7349 this.failureType = Roo.form.Action.LOAD_FAILURE;
7350 this.form.afterAction(this, false);
7353 this.form.clearInvalid();
7354 this.form.setValues(result.data);
7355 this.form.afterAction(this, true);
7358 handleResponse : function(response){
7359 if(this.form.reader){
7360 var rs = this.form.reader.read(response);
7361 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7363 success : rs.success,
7367 return Roo.decode(response.responseText);
7371 Roo.form.Action.ACTION_TYPES = {
7372 'load' : Roo.form.Action.Load,
7373 'submit' : Roo.form.Action.Submit
7382 * @class Roo.bootstrap.Form
7383 * @extends Roo.bootstrap.Component
7384 * Bootstrap Form class
7385 * @cfg {String} method GET | POST (default POST)
7386 * @cfg {String} labelAlign top | left (default top)
7387 * @cfg {String} align left | right - for navbars
7388 * @cfg {Boolean} loadMask load mask when submit (default true)
7393 * @param {Object} config The config object
7397 Roo.bootstrap.Form = function(config){
7398 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7401 * @event clientvalidation
7402 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7403 * @param {Form} this
7404 * @param {Boolean} valid true if the form has passed client-side validation
7406 clientvalidation: true,
7408 * @event beforeaction
7409 * Fires before any action is performed. Return false to cancel the action.
7410 * @param {Form} this
7411 * @param {Action} action The action to be performed
7415 * @event actionfailed
7416 * Fires when an action fails.
7417 * @param {Form} this
7418 * @param {Action} action The action that failed
7420 actionfailed : true,
7422 * @event actioncomplete
7423 * Fires when an action is completed.
7424 * @param {Form} this
7425 * @param {Action} action The action that completed
7427 actioncomplete : true
7432 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7435 * @cfg {String} method
7436 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7441 * The URL to use for form actions if one isn't supplied in the action options.
7444 * @cfg {Boolean} fileUpload
7445 * Set to true if this form is a file upload.
7449 * @cfg {Object} baseParams
7450 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7454 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7458 * @cfg {Sting} align (left|right) for navbar forms
7463 activeAction : null,
7466 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7467 * element by passing it or its id or mask the form itself by passing in true.
7470 waitMsgTarget : false,
7474 getAutoCreate : function(){
7478 method : this.method || 'POST',
7479 id : this.id || Roo.id(),
7482 if (this.parent().xtype.match(/^Nav/)) {
7483 cfg.cls = 'navbar-form navbar-' + this.align;
7487 if (this.labelAlign == 'left' ) {
7488 cfg.cls += ' form-horizontal';
7494 initEvents : function()
7496 this.el.on('submit', this.onSubmit, this);
7497 // this was added as random key presses on the form where triggering form submit.
7498 this.el.on('keypress', function(e) {
7499 if (e.getCharCode() != 13) {
7502 // we might need to allow it for textareas.. and some other items.
7503 // check e.getTarget().
7505 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7509 Roo.log("keypress blocked");
7517 onSubmit : function(e){
7522 * Returns true if client-side validation on the form is successful.
7525 isValid : function(){
7526 var items = this.getItems();
7528 items.each(function(f){
7537 * Returns true if any fields in this form have changed since their original load.
7540 isDirty : function(){
7542 var items = this.getItems();
7543 items.each(function(f){
7553 * Performs a predefined action (submit or load) or custom actions you define on this form.
7554 * @param {String} actionName The name of the action type
7555 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7556 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7557 * accept other config options):
7559 Property Type Description
7560 ---------------- --------------- ----------------------------------------------------------------------------------
7561 url String The url for the action (defaults to the form's url)
7562 method String The form method to use (defaults to the form's method, or POST if not defined)
7563 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7564 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7565 validate the form on the client (defaults to false)
7567 * @return {BasicForm} this
7569 doAction : function(action, options){
7570 if(typeof action == 'string'){
7571 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7573 if(this.fireEvent('beforeaction', this, action) !== false){
7574 this.beforeAction(action);
7575 action.run.defer(100, action);
7581 beforeAction : function(action){
7582 var o = action.options;
7585 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7587 // not really supported yet.. ??
7589 //if(this.waitMsgTarget === true){
7590 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7591 //}else if(this.waitMsgTarget){
7592 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7593 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7595 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7601 afterAction : function(action, success){
7602 this.activeAction = null;
7603 var o = action.options;
7605 //if(this.waitMsgTarget === true){
7607 //}else if(this.waitMsgTarget){
7608 // this.waitMsgTarget.unmask();
7610 // Roo.MessageBox.updateProgress(1);
7611 // Roo.MessageBox.hide();
7618 Roo.callback(o.success, o.scope, [this, action]);
7619 this.fireEvent('actioncomplete', this, action);
7623 // failure condition..
7624 // we have a scenario where updates need confirming.
7625 // eg. if a locking scenario exists..
7626 // we look for { errors : { needs_confirm : true }} in the response.
7628 (typeof(action.result) != 'undefined') &&
7629 (typeof(action.result.errors) != 'undefined') &&
7630 (typeof(action.result.errors.needs_confirm) != 'undefined')
7633 Roo.log("not supported yet");
7636 Roo.MessageBox.confirm(
7637 "Change requires confirmation",
7638 action.result.errorMsg,
7643 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7653 Roo.callback(o.failure, o.scope, [this, action]);
7654 // show an error message if no failed handler is set..
7655 if (!this.hasListener('actionfailed')) {
7656 Roo.log("need to add dialog support");
7658 Roo.MessageBox.alert("Error",
7659 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7660 action.result.errorMsg :
7661 "Saving Failed, please check your entries or try again"
7666 this.fireEvent('actionfailed', this, action);
7671 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7672 * @param {String} id The value to search for
7675 findField : function(id){
7676 var items = this.getItems();
7677 var field = items.get(id);
7679 items.each(function(f){
7680 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7687 return field || null;
7690 * Mark fields in this form invalid in bulk.
7691 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7692 * @return {BasicForm} this
7694 markInvalid : function(errors){
7695 if(errors instanceof Array){
7696 for(var i = 0, len = errors.length; i < len; i++){
7697 var fieldError = errors[i];
7698 var f = this.findField(fieldError.id);
7700 f.markInvalid(fieldError.msg);
7706 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7707 field.markInvalid(errors[id]);
7711 //Roo.each(this.childForms || [], function (f) {
7712 // f.markInvalid(errors);
7719 * Set values for fields in this form in bulk.
7720 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7721 * @return {BasicForm} this
7723 setValues : function(values){
7724 if(values instanceof Array){ // array of objects
7725 for(var i = 0, len = values.length; i < len; i++){
7727 var f = this.findField(v.id);
7729 f.setValue(v.value);
7730 if(this.trackResetOnLoad){
7731 f.originalValue = f.getValue();
7735 }else{ // object hash
7738 if(typeof values[id] != 'function' && (field = this.findField(id))){
7740 if (field.setFromData &&
7742 field.displayField &&
7743 // combos' with local stores can
7744 // be queried via setValue()
7745 // to set their value..
7746 (field.store && !field.store.isLocal)
7750 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7751 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7752 field.setFromData(sd);
7755 field.setValue(values[id]);
7759 if(this.trackResetOnLoad){
7760 field.originalValue = field.getValue();
7766 //Roo.each(this.childForms || [], function (f) {
7767 // f.setValues(values);
7774 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7775 * they are returned as an array.
7776 * @param {Boolean} asString
7779 getValues : function(asString){
7780 //if (this.childForms) {
7781 // copy values from the child forms
7782 // Roo.each(this.childForms, function (f) {
7783 // this.setValues(f.getValues());
7789 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7790 if(asString === true){
7793 return Roo.urlDecode(fs);
7797 * Returns the fields in this form as an object with key/value pairs.
7798 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7801 getFieldValues : function(with_hidden)
7803 var items = this.getItems();
7805 items.each(function(f){
7809 var v = f.getValue();
7810 if (f.inputType =='radio') {
7811 if (typeof(ret[f.getName()]) == 'undefined') {
7812 ret[f.getName()] = ''; // empty..
7815 if (!f.el.dom.checked) {
7823 // not sure if this supported any more..
7824 if ((typeof(v) == 'object') && f.getRawValue) {
7825 v = f.getRawValue() ; // dates..
7827 // combo boxes where name != hiddenName...
7828 if (f.name != f.getName()) {
7829 ret[f.name] = f.getRawValue();
7831 ret[f.getName()] = v;
7838 * Clears all invalid messages in this form.
7839 * @return {BasicForm} this
7841 clearInvalid : function(){
7842 var items = this.getItems();
7844 items.each(function(f){
7855 * @return {BasicForm} this
7858 var items = this.getItems();
7859 items.each(function(f){
7863 Roo.each(this.childForms || [], function (f) {
7870 getItems : function()
7872 var r=new Roo.util.MixedCollection(false, function(o){
7873 return o.id || (o.id = Roo.id());
7875 var iter = function(el) {
7882 Roo.each(el.items,function(e) {
7902 * Ext JS Library 1.1.1
7903 * Copyright(c) 2006-2007, Ext JS, LLC.
7905 * Originally Released Under LGPL - original licence link has changed is not relivant.
7908 * <script type="text/javascript">
7911 * @class Roo.form.VTypes
7912 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7915 Roo.form.VTypes = function(){
7916 // closure these in so they are only created once.
7917 var alpha = /^[a-zA-Z_]+$/;
7918 var alphanum = /^[a-zA-Z0-9_]+$/;
7919 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7920 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7922 // All these messages and functions are configurable
7925 * The function used to validate email addresses
7926 * @param {String} value The email address
7928 'email' : function(v){
7929 return email.test(v);
7932 * The error text to display when the email validation function returns false
7935 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7937 * The keystroke filter mask to be applied on email input
7940 'emailMask' : /[a-z0-9_\.\-@]/i,
7943 * The function used to validate URLs
7944 * @param {String} value The URL
7946 'url' : function(v){
7950 * The error text to display when the url validation function returns false
7953 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7956 * The function used to validate alpha values
7957 * @param {String} value The value
7959 'alpha' : function(v){
7960 return alpha.test(v);
7963 * The error text to display when the alpha validation function returns false
7966 'alphaText' : 'This field should only contain letters and _',
7968 * The keystroke filter mask to be applied on alpha input
7971 'alphaMask' : /[a-z_]/i,
7974 * The function used to validate alphanumeric values
7975 * @param {String} value The value
7977 'alphanum' : function(v){
7978 return alphanum.test(v);
7981 * The error text to display when the alphanumeric validation function returns false
7984 'alphanumText' : 'This field should only contain letters, numbers and _',
7986 * The keystroke filter mask to be applied on alphanumeric input
7989 'alphanumMask' : /[a-z0-9_]/i
7999 * @class Roo.bootstrap.Input
8000 * @extends Roo.bootstrap.Component
8001 * Bootstrap Input class
8002 * @cfg {Boolean} disabled is it disabled
8003 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8004 * @cfg {String} name name of the input
8005 * @cfg {string} fieldLabel - the label associated
8006 * @cfg {string} placeholder - placeholder to put in text.
8007 * @cfg {string} before - input group add on before
8008 * @cfg {string} after - input group add on after
8009 * @cfg {string} size - (lg|sm) or leave empty..
8010 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8011 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8012 * @cfg {Number} md colspan out of 12 for computer-sized screens
8013 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8014 * @cfg {string} value default value of the input
8015 * @cfg {Number} labelWidth set the width of label (0-12)
8016 * @cfg {String} labelAlign (top|left)
8017 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8018 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8019 * @cfg {String} indicatorpos (left|right) default left
8021 * @cfg {String} align (left|center|right) Default left
8022 * @cfg {Boolean} forceFeedback (true|false) Default false
8028 * Create a new Input
8029 * @param {Object} config The config object
8032 Roo.bootstrap.Input = function(config){
8033 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8038 * Fires when this field receives input focus.
8039 * @param {Roo.form.Field} this
8044 * Fires when this field loses input focus.
8045 * @param {Roo.form.Field} this
8050 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8051 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8052 * @param {Roo.form.Field} this
8053 * @param {Roo.EventObject} e The event object
8058 * Fires just before the field blurs if the field value has changed.
8059 * @param {Roo.form.Field} this
8060 * @param {Mixed} newValue The new value
8061 * @param {Mixed} oldValue The original value
8066 * Fires after the field has been marked as invalid.
8067 * @param {Roo.form.Field} this
8068 * @param {String} msg The validation message
8073 * Fires after the field has been validated with no errors.
8074 * @param {Roo.form.Field} this
8079 * Fires after the key up
8080 * @param {Roo.form.Field} this
8081 * @param {Roo.EventObject} e The event Object
8087 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8089 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8090 automatic validation (defaults to "keyup").
8092 validationEvent : "keyup",
8094 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8096 validateOnBlur : true,
8098 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8100 validationDelay : 250,
8102 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8104 focusClass : "x-form-focus", // not needed???
8108 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8110 invalidClass : "has-warning",
8113 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8115 validClass : "has-success",
8118 * @cfg {Boolean} hasFeedback (true|false) default true
8123 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8125 invalidFeedbackClass : "glyphicon-warning-sign",
8128 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8130 validFeedbackClass : "glyphicon-ok",
8133 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8135 selectOnFocus : false,
8138 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8142 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8147 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8149 disableKeyFilter : false,
8152 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8156 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8160 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8162 blankText : "This field is required",
8165 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8169 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8171 maxLength : Number.MAX_VALUE,
8173 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8175 minLengthText : "The minimum length for this field is {0}",
8177 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8179 maxLengthText : "The maximum length for this field is {0}",
8183 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8184 * If available, this function will be called only after the basic validators all return true, and will be passed the
8185 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8189 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8190 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8191 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8195 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8199 autocomplete: false,
8218 formatedValue : false,
8219 forceFeedback : false,
8221 indicatorpos : 'left',
8223 parentLabelAlign : function()
8226 while (parent.parent()) {
8227 parent = parent.parent();
8228 if (typeof(parent.labelAlign) !='undefined') {
8229 return parent.labelAlign;
8236 getAutoCreate : function()
8238 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8244 if(this.inputType != 'hidden'){
8245 cfg.cls = 'form-group' //input-group
8251 type : this.inputType,
8253 cls : 'form-control',
8254 placeholder : this.placeholder || '',
8255 autocomplete : this.autocomplete || 'new-password'
8259 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8262 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8263 input.maxLength = this.maxLength;
8266 if (this.disabled) {
8267 input.disabled=true;
8270 if (this.readOnly) {
8271 input.readonly=true;
8275 input.name = this.name;
8279 input.cls += ' input-' + this.size;
8283 ['xs','sm','md','lg'].map(function(size){
8284 if (settings[size]) {
8285 cfg.cls += ' col-' + size + '-' + settings[size];
8289 var inputblock = input;
8293 cls: 'glyphicon form-control-feedback'
8296 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8299 cls : 'has-feedback',
8307 if (this.before || this.after) {
8310 cls : 'input-group',
8314 if (this.before && typeof(this.before) == 'string') {
8316 inputblock.cn.push({
8318 cls : 'roo-input-before input-group-addon',
8322 if (this.before && typeof(this.before) == 'object') {
8323 this.before = Roo.factory(this.before);
8325 inputblock.cn.push({
8327 cls : 'roo-input-before input-group-' +
8328 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8332 inputblock.cn.push(input);
8334 if (this.after && typeof(this.after) == 'string') {
8335 inputblock.cn.push({
8337 cls : 'roo-input-after input-group-addon',
8341 if (this.after && typeof(this.after) == 'object') {
8342 this.after = Roo.factory(this.after);
8344 inputblock.cn.push({
8346 cls : 'roo-input-after input-group-' +
8347 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8351 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8352 inputblock.cls += ' has-feedback';
8353 inputblock.cn.push(feedback);
8357 if (align ==='left' && this.fieldLabel.length) {
8362 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8363 tooltip : 'This field is required'
8368 cls : 'control-label col-sm-' + this.labelWidth,
8369 html : this.fieldLabel
8373 cls : "col-sm-" + (12 - this.labelWidth),
8381 if(this.indicatorpos == 'right'){
8386 cls : 'control-label col-sm-' + this.labelWidth,
8387 html : this.fieldLabel
8392 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8393 tooltip : 'This field is required'
8396 cls : "col-sm-" + (12 - this.labelWidth),
8405 } else if ( this.fieldLabel.length) {
8410 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8411 tooltip : 'This field is required'
8415 //cls : 'input-group-addon',
8416 html : this.fieldLabel
8424 if(this.indicatorpos == 'right'){
8429 //cls : 'input-group-addon',
8430 html : this.fieldLabel
8435 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8436 tooltip : 'This field is required'
8456 if (this.parentType === 'Navbar' && this.parent().bar) {
8457 cfg.cls += ' navbar-form';
8460 if (this.parentType === 'NavGroup') {
8461 cfg.cls += ' navbar-form';
8469 * return the real input element.
8471 inputEl: function ()
8473 return this.el.select('input.form-control',true).first();
8476 tooltipEl : function()
8478 return this.inputEl();
8481 indicatorEl : function()
8483 var indicator = this.el.select('i.roo-required-indicator',true).first();
8493 setDisabled : function(v)
8495 var i = this.inputEl().dom;
8497 i.removeAttribute('disabled');
8501 i.setAttribute('disabled','true');
8503 initEvents : function()
8506 this.inputEl().on("keydown" , this.fireKey, this);
8507 this.inputEl().on("focus", this.onFocus, this);
8508 this.inputEl().on("blur", this.onBlur, this);
8510 this.inputEl().relayEvent('keyup', this);
8512 this.indicator = this.indicatorEl();
8515 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8516 this.indicator.hide();
8519 // reference to original value for reset
8520 this.originalValue = this.getValue();
8521 //Roo.form.TextField.superclass.initEvents.call(this);
8522 if(this.validationEvent == 'keyup'){
8523 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8524 this.inputEl().on('keyup', this.filterValidation, this);
8526 else if(this.validationEvent !== false){
8527 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8530 if(this.selectOnFocus){
8531 this.on("focus", this.preFocus, this);
8534 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8535 this.inputEl().on("keypress", this.filterKeys, this);
8538 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8539 this.el.on("click", this.autoSize, this);
8542 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8543 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8546 if (typeof(this.before) == 'object') {
8547 this.before.render(this.el.select('.roo-input-before',true).first());
8549 if (typeof(this.after) == 'object') {
8550 this.after.render(this.el.select('.roo-input-after',true).first());
8555 filterValidation : function(e){
8556 if(!e.isNavKeyPress()){
8557 this.validationTask.delay(this.validationDelay);
8561 * Validates the field value
8562 * @return {Boolean} True if the value is valid, else false
8564 validate : function(){
8565 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8566 if(this.disabled || this.validateValue(this.getRawValue())){
8577 * Validates a value according to the field's validation rules and marks the field as invalid
8578 * if the validation fails
8579 * @param {Mixed} value The value to validate
8580 * @return {Boolean} True if the value is valid, else false
8582 validateValue : function(value){
8583 if(value.length < 1) { // if it's blank
8584 if(this.allowBlank){
8590 if(value.length < this.minLength){
8593 if(value.length > this.maxLength){
8597 var vt = Roo.form.VTypes;
8598 if(!vt[this.vtype](value, this)){
8602 if(typeof this.validator == "function"){
8603 var msg = this.validator(value);
8609 if(this.regex && !this.regex.test(value)){
8619 fireKey : function(e){
8620 //Roo.log('field ' + e.getKey());
8621 if(e.isNavKeyPress()){
8622 this.fireEvent("specialkey", this, e);
8625 focus : function (selectText){
8627 this.inputEl().focus();
8628 if(selectText === true){
8629 this.inputEl().dom.select();
8635 onFocus : function(){
8636 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8637 // this.el.addClass(this.focusClass);
8640 this.hasFocus = true;
8641 this.startValue = this.getValue();
8642 this.fireEvent("focus", this);
8646 beforeBlur : Roo.emptyFn,
8650 onBlur : function(){
8652 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8653 //this.el.removeClass(this.focusClass);
8655 this.hasFocus = false;
8656 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8659 var v = this.getValue();
8660 if(String(v) !== String(this.startValue)){
8661 this.fireEvent('change', this, v, this.startValue);
8663 this.fireEvent("blur", this);
8667 * Resets the current field value to the originally loaded value and clears any validation messages
8670 this.setValue(this.originalValue);
8674 * Returns the name of the field
8675 * @return {Mixed} name The name field
8677 getName: function(){
8681 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8682 * @return {Mixed} value The field value
8684 getValue : function(){
8686 var v = this.inputEl().getValue();
8691 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8692 * @return {Mixed} value The field value
8694 getRawValue : function(){
8695 var v = this.inputEl().getValue();
8701 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8702 * @param {Mixed} value The value to set
8704 setRawValue : function(v){
8705 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8708 selectText : function(start, end){
8709 var v = this.getRawValue();
8711 start = start === undefined ? 0 : start;
8712 end = end === undefined ? v.length : end;
8713 var d = this.inputEl().dom;
8714 if(d.setSelectionRange){
8715 d.setSelectionRange(start, end);
8716 }else if(d.createTextRange){
8717 var range = d.createTextRange();
8718 range.moveStart("character", start);
8719 range.moveEnd("character", v.length-end);
8726 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8727 * @param {Mixed} value The value to set
8729 setValue : function(v){
8732 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8738 processValue : function(value){
8739 if(this.stripCharsRe){
8740 var newValue = value.replace(this.stripCharsRe, '');
8741 if(newValue !== value){
8742 this.setRawValue(newValue);
8749 preFocus : function(){
8751 if(this.selectOnFocus){
8752 this.inputEl().dom.select();
8755 filterKeys : function(e){
8757 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8760 var c = e.getCharCode(), cc = String.fromCharCode(c);
8761 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8764 if(!this.maskRe.test(cc)){
8769 * Clear any invalid styles/messages for this field
8771 clearInvalid : function(){
8773 if(!this.el || this.preventMark){ // not rendered
8778 this.indicator.hide();
8781 this.el.removeClass(this.invalidClass);
8783 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8785 var feedback = this.el.select('.form-control-feedback', true).first();
8788 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8793 this.fireEvent('valid', this);
8797 * Mark this field as valid
8799 markValid : function()
8801 if(!this.el || this.preventMark){ // not rendered
8805 this.el.removeClass([this.invalidClass, this.validClass]);
8807 var feedback = this.el.select('.form-control-feedback', true).first();
8810 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8813 if(this.disabled || this.allowBlank){
8818 this.indicator.hide();
8821 this.el.addClass(this.validClass);
8823 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8825 var feedback = this.el.select('.form-control-feedback', true).first();
8828 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8829 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8834 this.fireEvent('valid', this);
8838 * Mark this field as invalid
8839 * @param {String} msg The validation message
8841 markInvalid : function(msg)
8843 if(!this.el || this.preventMark){ // not rendered
8847 this.el.removeClass([this.invalidClass, this.validClass]);
8849 var feedback = this.el.select('.form-control-feedback', true).first();
8852 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8855 if(this.disabled || this.allowBlank){
8860 this.indicator.show();
8863 this.el.addClass(this.invalidClass);
8865 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8867 var feedback = this.el.select('.form-control-feedback', true).first();
8870 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8872 if(this.getValue().length || this.forceFeedback){
8873 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8880 this.fireEvent('invalid', this, msg);
8883 SafariOnKeyDown : function(event)
8885 // this is a workaround for a password hang bug on chrome/ webkit.
8887 var isSelectAll = false;
8889 if(this.inputEl().dom.selectionEnd > 0){
8890 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8892 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8893 event.preventDefault();
8898 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8900 event.preventDefault();
8901 // this is very hacky as keydown always get's upper case.
8903 var cc = String.fromCharCode(event.getCharCode());
8904 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8908 adjustWidth : function(tag, w){
8909 tag = tag.toLowerCase();
8910 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8911 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8915 if(tag == 'textarea'){
8918 }else if(Roo.isOpera){
8922 if(tag == 'textarea'){
8941 * @class Roo.bootstrap.TextArea
8942 * @extends Roo.bootstrap.Input
8943 * Bootstrap TextArea class
8944 * @cfg {Number} cols Specifies the visible width of a text area
8945 * @cfg {Number} rows Specifies the visible number of lines in a text area
8946 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8947 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8948 * @cfg {string} html text
8951 * Create a new TextArea
8952 * @param {Object} config The config object
8955 Roo.bootstrap.TextArea = function(config){
8956 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8960 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8970 getAutoCreate : function(){
8972 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8983 value : this.value || '',
8984 html: this.html || '',
8985 cls : 'form-control',
8986 placeholder : this.placeholder || ''
8990 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8991 input.maxLength = this.maxLength;
8995 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8999 input.cols = this.cols;
9002 if (this.readOnly) {
9003 input.readonly = true;
9007 input.name = this.name;
9011 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9015 ['xs','sm','md','lg'].map(function(size){
9016 if (settings[size]) {
9017 cfg.cls += ' col-' + size + '-' + settings[size];
9021 var inputblock = input;
9023 if(this.hasFeedback && !this.allowBlank){
9027 cls: 'glyphicon form-control-feedback'
9031 cls : 'has-feedback',
9040 if (this.before || this.after) {
9043 cls : 'input-group',
9047 inputblock.cn.push({
9049 cls : 'input-group-addon',
9054 inputblock.cn.push(input);
9056 if(this.hasFeedback && !this.allowBlank){
9057 inputblock.cls += ' has-feedback';
9058 inputblock.cn.push(feedback);
9062 inputblock.cn.push({
9064 cls : 'input-group-addon',
9071 if (align ==='left' && this.fieldLabel.length) {
9072 // Roo.log("left and has label");
9078 cls : 'control-label col-sm-' + this.labelWidth,
9079 html : this.fieldLabel
9083 cls : "col-sm-" + (12 - this.labelWidth),
9090 } else if ( this.fieldLabel.length) {
9091 // Roo.log(" label");
9096 //cls : 'input-group-addon',
9097 html : this.fieldLabel
9107 // Roo.log(" no label && no align");
9117 if (this.disabled) {
9118 input.disabled=true;
9125 * return the real textarea element.
9127 inputEl: function ()
9129 return this.el.select('textarea.form-control',true).first();
9133 * Clear any invalid styles/messages for this field
9135 clearInvalid : function()
9138 if(!this.el || this.preventMark){ // not rendered
9142 var label = this.el.select('label', true).first();
9143 var icon = this.el.select('i.fa-star', true).first();
9149 this.el.removeClass(this.invalidClass);
9151 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9153 var feedback = this.el.select('.form-control-feedback', true).first();
9156 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9161 this.fireEvent('valid', this);
9165 * Mark this field as valid
9167 markValid : function()
9169 if(!this.el || this.preventMark){ // not rendered
9173 this.el.removeClass([this.invalidClass, this.validClass]);
9175 var feedback = this.el.select('.form-control-feedback', true).first();
9178 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9181 if(this.disabled || this.allowBlank){
9185 var label = this.el.select('label', true).first();
9186 var icon = this.el.select('i.fa-star', true).first();
9192 this.el.addClass(this.validClass);
9194 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9196 var feedback = this.el.select('.form-control-feedback', true).first();
9199 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9200 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9205 this.fireEvent('valid', this);
9209 * Mark this field as invalid
9210 * @param {String} msg The validation message
9212 markInvalid : function(msg)
9214 if(!this.el || this.preventMark){ // not rendered
9218 this.el.removeClass([this.invalidClass, this.validClass]);
9220 var feedback = this.el.select('.form-control-feedback', true).first();
9223 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9226 if(this.disabled || this.allowBlank){
9230 var label = this.el.select('label', true).first();
9231 var icon = this.el.select('i.fa-star', true).first();
9233 if(!this.getValue().length && label && !icon){
9234 this.el.createChild({
9236 cls : 'text-danger fa fa-lg fa-star',
9237 tooltip : 'This field is required',
9238 style : 'margin-right:5px;'
9242 this.el.addClass(this.invalidClass);
9244 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9246 var feedback = this.el.select('.form-control-feedback', true).first();
9249 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9251 if(this.getValue().length || this.forceFeedback){
9252 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9259 this.fireEvent('invalid', this, msg);
9267 * trigger field - base class for combo..
9272 * @class Roo.bootstrap.TriggerField
9273 * @extends Roo.bootstrap.Input
9274 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9275 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9276 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9277 * for which you can provide a custom implementation. For example:
9279 var trigger = new Roo.bootstrap.TriggerField();
9280 trigger.onTriggerClick = myTriggerFn;
9281 trigger.applyTo('my-field');
9284 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9285 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9286 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9287 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9288 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9291 * Create a new TriggerField.
9292 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9293 * to the base TextField)
9295 Roo.bootstrap.TriggerField = function(config){
9296 this.mimicing = false;
9297 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9300 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9302 * @cfg {String} triggerClass A CSS class to apply to the trigger
9305 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9310 * @cfg {Boolean} removable (true|false) special filter default false
9314 /** @cfg {Boolean} grow @hide */
9315 /** @cfg {Number} growMin @hide */
9316 /** @cfg {Number} growMax @hide */
9322 autoSize: Roo.emptyFn,
9329 actionMode : 'wrap',
9334 getAutoCreate : function(){
9336 var align = this.labelAlign || this.parentLabelAlign();
9341 cls: 'form-group' //input-group
9348 type : this.inputType,
9349 cls : 'form-control',
9350 autocomplete: 'new-password',
9351 placeholder : this.placeholder || ''
9355 input.name = this.name;
9358 input.cls += ' input-' + this.size;
9361 if (this.disabled) {
9362 input.disabled=true;
9365 var inputblock = input;
9367 if(this.hasFeedback && !this.allowBlank){
9371 cls: 'glyphicon form-control-feedback'
9374 if(this.removable && !this.editable && !this.tickable){
9376 cls : 'has-feedback',
9382 cls : 'roo-combo-removable-btn close'
9389 cls : 'has-feedback',
9398 if(this.removable && !this.editable && !this.tickable){
9400 cls : 'roo-removable',
9406 cls : 'roo-combo-removable-btn close'
9413 if (this.before || this.after) {
9416 cls : 'input-group',
9420 inputblock.cn.push({
9422 cls : 'input-group-addon',
9427 inputblock.cn.push(input);
9429 if(this.hasFeedback && !this.allowBlank){
9430 inputblock.cls += ' has-feedback';
9431 inputblock.cn.push(feedback);
9435 inputblock.cn.push({
9437 cls : 'input-group-addon',
9450 cls: 'form-hidden-field'
9464 cls: 'form-hidden-field'
9468 cls: 'roo-select2-choices',
9472 cls: 'roo-select2-search-field',
9485 cls: 'roo-select2-container input-group',
9490 // cls: 'typeahead typeahead-long dropdown-menu',
9491 // style: 'display:none'
9496 if(!this.multiple && this.showToggleBtn){
9502 if (this.caret != false) {
9505 cls: 'fa fa-' + this.caret
9512 cls : 'input-group-addon btn dropdown-toggle',
9517 cls: 'combobox-clear',
9531 combobox.cls += ' roo-select2-container-multi';
9534 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9536 // Roo.log("left and has label");
9540 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9541 tooltip : 'This field is required'
9546 cls : 'control-label col-sm-' + this.labelWidth,
9547 html : this.fieldLabel
9551 cls : "col-sm-" + (12 - this.labelWidth),
9559 if(this.indicatorpos == 'right'){
9564 cls : 'control-label col-sm-' + this.labelWidth,
9565 html : this.fieldLabel
9570 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9571 tooltip : 'This field is required'
9574 cls : "col-sm-" + (12 - this.labelWidth),
9583 } else if ( this.fieldLabel.length) {
9584 // Roo.log(" label");
9588 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9589 tooltip : 'This field is required'
9593 //cls : 'input-group-addon',
9594 html : this.fieldLabel
9602 if(this.indicatorpos == 'right'){
9607 //cls : 'input-group-addon',
9608 html : this.fieldLabel
9613 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9614 tooltip : 'This field is required'
9625 // Roo.log(" no label && no align");
9632 ['xs','sm','md','lg'].map(function(size){
9633 if (settings[size]) {
9634 cfg.cls += ' col-' + size + '-' + settings[size];
9645 onResize : function(w, h){
9646 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9647 // if(typeof w == 'number'){
9648 // var x = w - this.trigger.getWidth();
9649 // this.inputEl().setWidth(this.adjustWidth('input', x));
9650 // this.trigger.setStyle('left', x+'px');
9655 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9658 getResizeEl : function(){
9659 return this.inputEl();
9663 getPositionEl : function(){
9664 return this.inputEl();
9668 alignErrorIcon : function(){
9669 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9673 initEvents : function(){
9677 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9678 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9679 if(!this.multiple && this.showToggleBtn){
9680 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9681 if(this.hideTrigger){
9682 this.trigger.setDisplayed(false);
9684 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9688 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9691 if(this.removable && !this.editable && !this.tickable){
9692 var close = this.closeTriggerEl();
9695 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9696 close.on('click', this.removeBtnClick, this, close);
9700 //this.trigger.addClassOnOver('x-form-trigger-over');
9701 //this.trigger.addClassOnClick('x-form-trigger-click');
9704 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9708 closeTriggerEl : function()
9710 var close = this.el.select('.roo-combo-removable-btn', true).first();
9711 return close ? close : false;
9714 removeBtnClick : function(e, h, el)
9718 if(this.fireEvent("remove", this) !== false){
9720 this.fireEvent("afterremove", this)
9724 createList : function()
9726 this.list = Roo.get(document.body).createChild({
9728 cls: 'typeahead typeahead-long dropdown-menu',
9729 style: 'display:none'
9732 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9737 initTrigger : function(){
9742 onDestroy : function(){
9744 this.trigger.removeAllListeners();
9745 // this.trigger.remove();
9748 // this.wrap.remove();
9750 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9754 onFocus : function(){
9755 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9758 this.wrap.addClass('x-trigger-wrap-focus');
9759 this.mimicing = true;
9760 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9761 if(this.monitorTab){
9762 this.el.on("keydown", this.checkTab, this);
9769 checkTab : function(e){
9770 if(e.getKey() == e.TAB){
9776 onBlur : function(){
9781 mimicBlur : function(e, t){
9783 if(!this.wrap.contains(t) && this.validateBlur()){
9790 triggerBlur : function(){
9791 this.mimicing = false;
9792 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9793 if(this.monitorTab){
9794 this.el.un("keydown", this.checkTab, this);
9796 //this.wrap.removeClass('x-trigger-wrap-focus');
9797 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9801 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9802 validateBlur : function(e, t){
9807 onDisable : function(){
9808 this.inputEl().dom.disabled = true;
9809 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9811 // this.wrap.addClass('x-item-disabled');
9816 onEnable : function(){
9817 this.inputEl().dom.disabled = false;
9818 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9820 // this.el.removeClass('x-item-disabled');
9825 onShow : function(){
9826 var ae = this.getActionEl();
9829 ae.dom.style.display = '';
9830 ae.dom.style.visibility = 'visible';
9836 onHide : function(){
9837 var ae = this.getActionEl();
9838 ae.dom.style.display = 'none';
9842 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9843 * by an implementing function.
9845 * @param {EventObject} e
9847 onTriggerClick : Roo.emptyFn
9851 * Ext JS Library 1.1.1
9852 * Copyright(c) 2006-2007, Ext JS, LLC.
9854 * Originally Released Under LGPL - original licence link has changed is not relivant.
9857 * <script type="text/javascript">
9862 * @class Roo.data.SortTypes
9864 * Defines the default sorting (casting?) comparison functions used when sorting data.
9866 Roo.data.SortTypes = {
9868 * Default sort that does nothing
9869 * @param {Mixed} s The value being converted
9870 * @return {Mixed} The comparison value
9877 * The regular expression used to strip tags
9881 stripTagsRE : /<\/?[^>]+>/gi,
9884 * Strips all HTML tags to sort on text only
9885 * @param {Mixed} s The value being converted
9886 * @return {String} The comparison value
9888 asText : function(s){
9889 return String(s).replace(this.stripTagsRE, "");
9893 * Strips all HTML tags to sort on text only - Case insensitive
9894 * @param {Mixed} s The value being converted
9895 * @return {String} The comparison value
9897 asUCText : function(s){
9898 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9902 * Case insensitive string
9903 * @param {Mixed} s The value being converted
9904 * @return {String} The comparison value
9906 asUCString : function(s) {
9907 return String(s).toUpperCase();
9912 * @param {Mixed} s The value being converted
9913 * @return {Number} The comparison value
9915 asDate : function(s) {
9919 if(s instanceof Date){
9922 return Date.parse(String(s));
9927 * @param {Mixed} s The value being converted
9928 * @return {Float} The comparison value
9930 asFloat : function(s) {
9931 var val = parseFloat(String(s).replace(/,/g, ""));
9940 * @param {Mixed} s The value being converted
9941 * @return {Number} The comparison value
9943 asInt : function(s) {
9944 var val = parseInt(String(s).replace(/,/g, ""));
9952 * Ext JS Library 1.1.1
9953 * Copyright(c) 2006-2007, Ext JS, LLC.
9955 * Originally Released Under LGPL - original licence link has changed is not relivant.
9958 * <script type="text/javascript">
9962 * @class Roo.data.Record
9963 * Instances of this class encapsulate both record <em>definition</em> information, and record
9964 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9965 * to access Records cached in an {@link Roo.data.Store} object.<br>
9967 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9968 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9971 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9973 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9974 * {@link #create}. The parameters are the same.
9975 * @param {Array} data An associative Array of data values keyed by the field name.
9976 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9977 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9978 * not specified an integer id is generated.
9980 Roo.data.Record = function(data, id){
9981 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9986 * Generate a constructor for a specific record layout.
9987 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9988 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9989 * Each field definition object may contain the following properties: <ul>
9990 * <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,
9991 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9992 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9993 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9994 * is being used, then this is a string containing the javascript expression to reference the data relative to
9995 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9996 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9997 * this may be omitted.</p></li>
9998 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9999 * <ul><li>auto (Default, implies no conversion)</li>
10004 * <li>date</li></ul></p></li>
10005 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10006 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10007 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10008 * by the Reader into an object that will be stored in the Record. It is passed the
10009 * following parameters:<ul>
10010 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10012 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10014 * <br>usage:<br><pre><code>
10015 var TopicRecord = Roo.data.Record.create(
10016 {name: 'title', mapping: 'topic_title'},
10017 {name: 'author', mapping: 'username'},
10018 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10019 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10020 {name: 'lastPoster', mapping: 'user2'},
10021 {name: 'excerpt', mapping: 'post_text'}
10024 var myNewRecord = new TopicRecord({
10025 title: 'Do my job please',
10028 lastPost: new Date(),
10029 lastPoster: 'Animal',
10030 excerpt: 'No way dude!'
10032 myStore.add(myNewRecord);
10037 Roo.data.Record.create = function(o){
10038 var f = function(){
10039 f.superclass.constructor.apply(this, arguments);
10041 Roo.extend(f, Roo.data.Record);
10042 var p = f.prototype;
10043 p.fields = new Roo.util.MixedCollection(false, function(field){
10046 for(var i = 0, len = o.length; i < len; i++){
10047 p.fields.add(new Roo.data.Field(o[i]));
10049 f.getField = function(name){
10050 return p.fields.get(name);
10055 Roo.data.Record.AUTO_ID = 1000;
10056 Roo.data.Record.EDIT = 'edit';
10057 Roo.data.Record.REJECT = 'reject';
10058 Roo.data.Record.COMMIT = 'commit';
10060 Roo.data.Record.prototype = {
10062 * Readonly flag - true if this record has been modified.
10071 join : function(store){
10072 this.store = store;
10076 * Set the named field to the specified value.
10077 * @param {String} name The name of the field to set.
10078 * @param {Object} value The value to set the field to.
10080 set : function(name, value){
10081 if(this.data[name] == value){
10085 if(!this.modified){
10086 this.modified = {};
10088 if(typeof this.modified[name] == 'undefined'){
10089 this.modified[name] = this.data[name];
10091 this.data[name] = value;
10092 if(!this.editing && this.store){
10093 this.store.afterEdit(this);
10098 * Get the value of the named field.
10099 * @param {String} name The name of the field to get the value of.
10100 * @return {Object} The value of the field.
10102 get : function(name){
10103 return this.data[name];
10107 beginEdit : function(){
10108 this.editing = true;
10109 this.modified = {};
10113 cancelEdit : function(){
10114 this.editing = false;
10115 delete this.modified;
10119 endEdit : function(){
10120 this.editing = false;
10121 if(this.dirty && this.store){
10122 this.store.afterEdit(this);
10127 * Usually called by the {@link Roo.data.Store} which owns the Record.
10128 * Rejects all changes made to the Record since either creation, or the last commit operation.
10129 * Modified fields are reverted to their original values.
10131 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10132 * of reject operations.
10134 reject : function(){
10135 var m = this.modified;
10137 if(typeof m[n] != "function"){
10138 this.data[n] = m[n];
10141 this.dirty = false;
10142 delete this.modified;
10143 this.editing = false;
10145 this.store.afterReject(this);
10150 * Usually called by the {@link Roo.data.Store} which owns the Record.
10151 * Commits all changes made to the Record since either creation, or the last commit operation.
10153 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10154 * of commit operations.
10156 commit : function(){
10157 this.dirty = false;
10158 delete this.modified;
10159 this.editing = false;
10161 this.store.afterCommit(this);
10166 hasError : function(){
10167 return this.error != null;
10171 clearError : function(){
10176 * Creates a copy of this record.
10177 * @param {String} id (optional) A new record id if you don't want to use this record's id
10180 copy : function(newId) {
10181 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10185 * Ext JS Library 1.1.1
10186 * Copyright(c) 2006-2007, Ext JS, LLC.
10188 * Originally Released Under LGPL - original licence link has changed is not relivant.
10191 * <script type="text/javascript">
10197 * @class Roo.data.Store
10198 * @extends Roo.util.Observable
10199 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10200 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10202 * 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
10203 * has no knowledge of the format of the data returned by the Proxy.<br>
10205 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10206 * instances from the data object. These records are cached and made available through accessor functions.
10208 * Creates a new Store.
10209 * @param {Object} config A config object containing the objects needed for the Store to access data,
10210 * and read the data into Records.
10212 Roo.data.Store = function(config){
10213 this.data = new Roo.util.MixedCollection(false);
10214 this.data.getKey = function(o){
10217 this.baseParams = {};
10219 this.paramNames = {
10224 "multisort" : "_multisort"
10227 if(config && config.data){
10228 this.inlineData = config.data;
10229 delete config.data;
10232 Roo.apply(this, config);
10234 if(this.reader){ // reader passed
10235 this.reader = Roo.factory(this.reader, Roo.data);
10236 this.reader.xmodule = this.xmodule || false;
10237 if(!this.recordType){
10238 this.recordType = this.reader.recordType;
10240 if(this.reader.onMetaChange){
10241 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10245 if(this.recordType){
10246 this.fields = this.recordType.prototype.fields;
10248 this.modified = [];
10252 * @event datachanged
10253 * Fires when the data cache has changed, and a widget which is using this Store
10254 * as a Record cache should refresh its view.
10255 * @param {Store} this
10257 datachanged : true,
10259 * @event metachange
10260 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10261 * @param {Store} this
10262 * @param {Object} meta The JSON metadata
10267 * Fires when Records have been added to the Store
10268 * @param {Store} this
10269 * @param {Roo.data.Record[]} records The array of Records added
10270 * @param {Number} index The index at which the record(s) were added
10275 * Fires when a Record has been removed from the Store
10276 * @param {Store} this
10277 * @param {Roo.data.Record} record The Record that was removed
10278 * @param {Number} index The index at which the record was removed
10283 * Fires when a Record has been updated
10284 * @param {Store} this
10285 * @param {Roo.data.Record} record The Record that was updated
10286 * @param {String} operation The update operation being performed. Value may be one of:
10288 Roo.data.Record.EDIT
10289 Roo.data.Record.REJECT
10290 Roo.data.Record.COMMIT
10296 * Fires when the data cache has been cleared.
10297 * @param {Store} this
10301 * @event beforeload
10302 * Fires before a request is made for a new data object. If the beforeload handler returns false
10303 * the load action will be canceled.
10304 * @param {Store} this
10305 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10309 * @event beforeloadadd
10310 * Fires after a new set of Records has been loaded.
10311 * @param {Store} this
10312 * @param {Roo.data.Record[]} records The Records that were loaded
10313 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10315 beforeloadadd : true,
10318 * Fires after a new set of Records has been loaded, before they are added to the store.
10319 * @param {Store} this
10320 * @param {Roo.data.Record[]} records The Records that were loaded
10321 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10322 * @params {Object} return from reader
10326 * @event loadexception
10327 * Fires if an exception occurs in the Proxy during loading.
10328 * Called with the signature of the Proxy's "loadexception" event.
10329 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10332 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10333 * @param {Object} load options
10334 * @param {Object} jsonData from your request (normally this contains the Exception)
10336 loadexception : true
10340 this.proxy = Roo.factory(this.proxy, Roo.data);
10341 this.proxy.xmodule = this.xmodule || false;
10342 this.relayEvents(this.proxy, ["loadexception"]);
10344 this.sortToggle = {};
10345 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10347 Roo.data.Store.superclass.constructor.call(this);
10349 if(this.inlineData){
10350 this.loadData(this.inlineData);
10351 delete this.inlineData;
10355 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10357 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10358 * without a remote query - used by combo/forms at present.
10362 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10365 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10368 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10369 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10372 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10373 * on any HTTP request
10376 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10379 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10383 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10384 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10386 remoteSort : false,
10389 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10390 * loaded or when a record is removed. (defaults to false).
10392 pruneModifiedRecords : false,
10395 lastOptions : null,
10398 * Add Records to the Store and fires the add event.
10399 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10401 add : function(records){
10402 records = [].concat(records);
10403 for(var i = 0, len = records.length; i < len; i++){
10404 records[i].join(this);
10406 var index = this.data.length;
10407 this.data.addAll(records);
10408 this.fireEvent("add", this, records, index);
10412 * Remove a Record from the Store and fires the remove event.
10413 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10415 remove : function(record){
10416 var index = this.data.indexOf(record);
10417 this.data.removeAt(index);
10418 if(this.pruneModifiedRecords){
10419 this.modified.remove(record);
10421 this.fireEvent("remove", this, record, index);
10425 * Remove all Records from the Store and fires the clear event.
10427 removeAll : function(){
10429 if(this.pruneModifiedRecords){
10430 this.modified = [];
10432 this.fireEvent("clear", this);
10436 * Inserts Records to the Store at the given index and fires the add event.
10437 * @param {Number} index The start index at which to insert the passed Records.
10438 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10440 insert : function(index, records){
10441 records = [].concat(records);
10442 for(var i = 0, len = records.length; i < len; i++){
10443 this.data.insert(index, records[i]);
10444 records[i].join(this);
10446 this.fireEvent("add", this, records, index);
10450 * Get the index within the cache of the passed Record.
10451 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10452 * @return {Number} The index of the passed Record. Returns -1 if not found.
10454 indexOf : function(record){
10455 return this.data.indexOf(record);
10459 * Get the index within the cache of the Record with the passed id.
10460 * @param {String} id The id of the Record to find.
10461 * @return {Number} The index of the Record. Returns -1 if not found.
10463 indexOfId : function(id){
10464 return this.data.indexOfKey(id);
10468 * Get the Record with the specified id.
10469 * @param {String} id The id of the Record to find.
10470 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10472 getById : function(id){
10473 return this.data.key(id);
10477 * Get the Record at the specified index.
10478 * @param {Number} index The index of the Record to find.
10479 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10481 getAt : function(index){
10482 return this.data.itemAt(index);
10486 * Returns a range of Records between specified indices.
10487 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10488 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10489 * @return {Roo.data.Record[]} An array of Records
10491 getRange : function(start, end){
10492 return this.data.getRange(start, end);
10496 storeOptions : function(o){
10497 o = Roo.apply({}, o);
10500 this.lastOptions = o;
10504 * Loads the Record cache from the configured Proxy using the configured Reader.
10506 * If using remote paging, then the first load call must specify the <em>start</em>
10507 * and <em>limit</em> properties in the options.params property to establish the initial
10508 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10510 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10511 * and this call will return before the new data has been loaded. Perform any post-processing
10512 * in a callback function, or in a "load" event handler.</strong>
10514 * @param {Object} options An object containing properties which control loading options:<ul>
10515 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10516 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10517 * passed the following arguments:<ul>
10518 * <li>r : Roo.data.Record[]</li>
10519 * <li>options: Options object from the load call</li>
10520 * <li>success: Boolean success indicator</li></ul></li>
10521 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10522 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10525 load : function(options){
10526 options = options || {};
10527 if(this.fireEvent("beforeload", this, options) !== false){
10528 this.storeOptions(options);
10529 var p = Roo.apply(options.params || {}, this.baseParams);
10530 // if meta was not loaded from remote source.. try requesting it.
10531 if (!this.reader.metaFromRemote) {
10532 p._requestMeta = 1;
10534 if(this.sortInfo && this.remoteSort){
10535 var pn = this.paramNames;
10536 p[pn["sort"]] = this.sortInfo.field;
10537 p[pn["dir"]] = this.sortInfo.direction;
10539 if (this.multiSort) {
10540 var pn = this.paramNames;
10541 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10544 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10549 * Reloads the Record cache from the configured Proxy using the configured Reader and
10550 * the options from the last load operation performed.
10551 * @param {Object} options (optional) An object containing properties which may override the options
10552 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10553 * the most recently used options are reused).
10555 reload : function(options){
10556 this.load(Roo.applyIf(options||{}, this.lastOptions));
10560 // Called as a callback by the Reader during a load operation.
10561 loadRecords : function(o, options, success){
10562 if(!o || success === false){
10563 if(success !== false){
10564 this.fireEvent("load", this, [], options, o);
10566 if(options.callback){
10567 options.callback.call(options.scope || this, [], options, false);
10571 // if data returned failure - throw an exception.
10572 if (o.success === false) {
10573 // show a message if no listener is registered.
10574 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10575 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10577 // loadmask wil be hooked into this..
10578 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10581 var r = o.records, t = o.totalRecords || r.length;
10583 this.fireEvent("beforeloadadd", this, r, options, o);
10585 if(!options || options.add !== true){
10586 if(this.pruneModifiedRecords){
10587 this.modified = [];
10589 for(var i = 0, len = r.length; i < len; i++){
10593 this.data = this.snapshot;
10594 delete this.snapshot;
10597 this.data.addAll(r);
10598 this.totalLength = t;
10600 this.fireEvent("datachanged", this);
10602 this.totalLength = Math.max(t, this.data.length+r.length);
10605 this.fireEvent("load", this, r, options, o);
10606 if(options.callback){
10607 options.callback.call(options.scope || this, r, options, true);
10613 * Loads data from a passed data block. A Reader which understands the format of the data
10614 * must have been configured in the constructor.
10615 * @param {Object} data The data block from which to read the Records. The format of the data expected
10616 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10617 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10619 loadData : function(o, append){
10620 var r = this.reader.readRecords(o);
10621 this.loadRecords(r, {add: append}, true);
10625 * Gets the number of cached records.
10627 * <em>If using paging, this may not be the total size of the dataset. If the data object
10628 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10629 * the data set size</em>
10631 getCount : function(){
10632 return this.data.length || 0;
10636 * Gets the total number of records in the dataset as returned by the server.
10638 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10639 * the dataset size</em>
10641 getTotalCount : function(){
10642 return this.totalLength || 0;
10646 * Returns the sort state of the Store as an object with two properties:
10648 field {String} The name of the field by which the Records are sorted
10649 direction {String} The sort order, "ASC" or "DESC"
10652 getSortState : function(){
10653 return this.sortInfo;
10657 applySort : function(){
10658 if(this.sortInfo && !this.remoteSort){
10659 var s = this.sortInfo, f = s.field;
10660 var st = this.fields.get(f).sortType;
10661 var fn = function(r1, r2){
10662 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10663 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10665 this.data.sort(s.direction, fn);
10666 if(this.snapshot && this.snapshot != this.data){
10667 this.snapshot.sort(s.direction, fn);
10673 * Sets the default sort column and order to be used by the next load operation.
10674 * @param {String} fieldName The name of the field to sort by.
10675 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10677 setDefaultSort : function(field, dir){
10678 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10682 * Sort the Records.
10683 * If remote sorting is used, the sort is performed on the server, and the cache is
10684 * reloaded. If local sorting is used, the cache is sorted internally.
10685 * @param {String} fieldName The name of the field to sort by.
10686 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10688 sort : function(fieldName, dir){
10689 var f = this.fields.get(fieldName);
10691 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10693 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10694 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10699 this.sortToggle[f.name] = dir;
10700 this.sortInfo = {field: f.name, direction: dir};
10701 if(!this.remoteSort){
10703 this.fireEvent("datachanged", this);
10705 this.load(this.lastOptions);
10710 * Calls the specified function for each of the Records in the cache.
10711 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10712 * Returning <em>false</em> aborts and exits the iteration.
10713 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10715 each : function(fn, scope){
10716 this.data.each(fn, scope);
10720 * Gets all records modified since the last commit. Modified records are persisted across load operations
10721 * (e.g., during paging).
10722 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10724 getModifiedRecords : function(){
10725 return this.modified;
10729 createFilterFn : function(property, value, anyMatch){
10730 if(!value.exec){ // not a regex
10731 value = String(value);
10732 if(value.length == 0){
10735 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10737 return function(r){
10738 return value.test(r.data[property]);
10743 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10744 * @param {String} property A field on your records
10745 * @param {Number} start The record index to start at (defaults to 0)
10746 * @param {Number} end The last record index to include (defaults to length - 1)
10747 * @return {Number} The sum
10749 sum : function(property, start, end){
10750 var rs = this.data.items, v = 0;
10751 start = start || 0;
10752 end = (end || end === 0) ? end : rs.length-1;
10754 for(var i = start; i <= end; i++){
10755 v += (rs[i].data[property] || 0);
10761 * Filter the records by a specified property.
10762 * @param {String} field A field on your records
10763 * @param {String/RegExp} value Either a string that the field
10764 * should start with or a RegExp to test against the field
10765 * @param {Boolean} anyMatch True to match any part not just the beginning
10767 filter : function(property, value, anyMatch){
10768 var fn = this.createFilterFn(property, value, anyMatch);
10769 return fn ? this.filterBy(fn) : this.clearFilter();
10773 * Filter by a function. The specified function will be called with each
10774 * record in this data source. If the function returns true the record is included,
10775 * otherwise it is filtered.
10776 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10777 * @param {Object} scope (optional) The scope of the function (defaults to this)
10779 filterBy : function(fn, scope){
10780 this.snapshot = this.snapshot || this.data;
10781 this.data = this.queryBy(fn, scope||this);
10782 this.fireEvent("datachanged", this);
10786 * Query the records by a specified property.
10787 * @param {String} field A field on your records
10788 * @param {String/RegExp} value Either a string that the field
10789 * should start with or a RegExp to test against the field
10790 * @param {Boolean} anyMatch True to match any part not just the beginning
10791 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10793 query : function(property, value, anyMatch){
10794 var fn = this.createFilterFn(property, value, anyMatch);
10795 return fn ? this.queryBy(fn) : this.data.clone();
10799 * Query by a function. The specified function will be called with each
10800 * record in this data source. If the function returns true the record is included
10802 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10803 * @param {Object} scope (optional) The scope of the function (defaults to this)
10804 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10806 queryBy : function(fn, scope){
10807 var data = this.snapshot || this.data;
10808 return data.filterBy(fn, scope||this);
10812 * Collects unique values for a particular dataIndex from this store.
10813 * @param {String} dataIndex The property to collect
10814 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10815 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10816 * @return {Array} An array of the unique values
10818 collect : function(dataIndex, allowNull, bypassFilter){
10819 var d = (bypassFilter === true && this.snapshot) ?
10820 this.snapshot.items : this.data.items;
10821 var v, sv, r = [], l = {};
10822 for(var i = 0, len = d.length; i < len; i++){
10823 v = d[i].data[dataIndex];
10825 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10834 * Revert to a view of the Record cache with no filtering applied.
10835 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10837 clearFilter : function(suppressEvent){
10838 if(this.snapshot && this.snapshot != this.data){
10839 this.data = this.snapshot;
10840 delete this.snapshot;
10841 if(suppressEvent !== true){
10842 this.fireEvent("datachanged", this);
10848 afterEdit : function(record){
10849 if(this.modified.indexOf(record) == -1){
10850 this.modified.push(record);
10852 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10856 afterReject : function(record){
10857 this.modified.remove(record);
10858 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10862 afterCommit : function(record){
10863 this.modified.remove(record);
10864 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10868 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10869 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10871 commitChanges : function(){
10872 var m = this.modified.slice(0);
10873 this.modified = [];
10874 for(var i = 0, len = m.length; i < len; i++){
10880 * Cancel outstanding changes on all changed records.
10882 rejectChanges : function(){
10883 var m = this.modified.slice(0);
10884 this.modified = [];
10885 for(var i = 0, len = m.length; i < len; i++){
10890 onMetaChange : function(meta, rtype, o){
10891 this.recordType = rtype;
10892 this.fields = rtype.prototype.fields;
10893 delete this.snapshot;
10894 this.sortInfo = meta.sortInfo || this.sortInfo;
10895 this.modified = [];
10896 this.fireEvent('metachange', this, this.reader.meta);
10899 moveIndex : function(data, type)
10901 var index = this.indexOf(data);
10903 var newIndex = index + type;
10907 this.insert(newIndex, data);
10912 * Ext JS Library 1.1.1
10913 * Copyright(c) 2006-2007, Ext JS, LLC.
10915 * Originally Released Under LGPL - original licence link has changed is not relivant.
10918 * <script type="text/javascript">
10922 * @class Roo.data.SimpleStore
10923 * @extends Roo.data.Store
10924 * Small helper class to make creating Stores from Array data easier.
10925 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10926 * @cfg {Array} fields An array of field definition objects, or field name strings.
10927 * @cfg {Array} data The multi-dimensional array of data
10929 * @param {Object} config
10931 Roo.data.SimpleStore = function(config){
10932 Roo.data.SimpleStore.superclass.constructor.call(this, {
10934 reader: new Roo.data.ArrayReader({
10937 Roo.data.Record.create(config.fields)
10939 proxy : new Roo.data.MemoryProxy(config.data)
10943 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10945 * Ext JS Library 1.1.1
10946 * Copyright(c) 2006-2007, Ext JS, LLC.
10948 * Originally Released Under LGPL - original licence link has changed is not relivant.
10951 * <script type="text/javascript">
10956 * @extends Roo.data.Store
10957 * @class Roo.data.JsonStore
10958 * Small helper class to make creating Stores for JSON data easier. <br/>
10960 var store = new Roo.data.JsonStore({
10961 url: 'get-images.php',
10963 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10966 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10967 * JsonReader and HttpProxy (unless inline data is provided).</b>
10968 * @cfg {Array} fields An array of field definition objects, or field name strings.
10970 * @param {Object} config
10972 Roo.data.JsonStore = function(c){
10973 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10974 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10975 reader: new Roo.data.JsonReader(c, c.fields)
10978 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10980 * Ext JS Library 1.1.1
10981 * Copyright(c) 2006-2007, Ext JS, LLC.
10983 * Originally Released Under LGPL - original licence link has changed is not relivant.
10986 * <script type="text/javascript">
10990 Roo.data.Field = function(config){
10991 if(typeof config == "string"){
10992 config = {name: config};
10994 Roo.apply(this, config);
10997 this.type = "auto";
11000 var st = Roo.data.SortTypes;
11001 // named sortTypes are supported, here we look them up
11002 if(typeof this.sortType == "string"){
11003 this.sortType = st[this.sortType];
11006 // set default sortType for strings and dates
11007 if(!this.sortType){
11010 this.sortType = st.asUCString;
11013 this.sortType = st.asDate;
11016 this.sortType = st.none;
11021 var stripRe = /[\$,%]/g;
11023 // prebuilt conversion function for this field, instead of
11024 // switching every time we're reading a value
11026 var cv, dateFormat = this.dateFormat;
11031 cv = function(v){ return v; };
11034 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11038 return v !== undefined && v !== null && v !== '' ?
11039 parseInt(String(v).replace(stripRe, ""), 10) : '';
11044 return v !== undefined && v !== null && v !== '' ?
11045 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11050 cv = function(v){ return v === true || v === "true" || v == 1; };
11057 if(v instanceof Date){
11061 if(dateFormat == "timestamp"){
11062 return new Date(v*1000);
11064 return Date.parseDate(v, dateFormat);
11066 var parsed = Date.parse(v);
11067 return parsed ? new Date(parsed) : null;
11076 Roo.data.Field.prototype = {
11084 * Ext JS Library 1.1.1
11085 * Copyright(c) 2006-2007, Ext JS, LLC.
11087 * Originally Released Under LGPL - original licence link has changed is not relivant.
11090 * <script type="text/javascript">
11093 // Base class for reading structured data from a data source. This class is intended to be
11094 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11097 * @class Roo.data.DataReader
11098 * Base class for reading structured data from a data source. This class is intended to be
11099 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11102 Roo.data.DataReader = function(meta, recordType){
11106 this.recordType = recordType instanceof Array ?
11107 Roo.data.Record.create(recordType) : recordType;
11110 Roo.data.DataReader.prototype = {
11112 * Create an empty record
11113 * @param {Object} data (optional) - overlay some values
11114 * @return {Roo.data.Record} record created.
11116 newRow : function(d) {
11118 this.recordType.prototype.fields.each(function(c) {
11120 case 'int' : da[c.name] = 0; break;
11121 case 'date' : da[c.name] = new Date(); break;
11122 case 'float' : da[c.name] = 0.0; break;
11123 case 'boolean' : da[c.name] = false; break;
11124 default : da[c.name] = ""; break;
11128 return new this.recordType(Roo.apply(da, d));
11133 * Ext JS Library 1.1.1
11134 * Copyright(c) 2006-2007, Ext JS, LLC.
11136 * Originally Released Under LGPL - original licence link has changed is not relivant.
11139 * <script type="text/javascript">
11143 * @class Roo.data.DataProxy
11144 * @extends Roo.data.Observable
11145 * This class is an abstract base class for implementations which provide retrieval of
11146 * unformatted data objects.<br>
11148 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11149 * (of the appropriate type which knows how to parse the data object) to provide a block of
11150 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11152 * Custom implementations must implement the load method as described in
11153 * {@link Roo.data.HttpProxy#load}.
11155 Roo.data.DataProxy = function(){
11158 * @event beforeload
11159 * Fires before a network request is made to retrieve a data object.
11160 * @param {Object} This DataProxy object.
11161 * @param {Object} params The params parameter to the load function.
11166 * Fires before the load method's callback is called.
11167 * @param {Object} This DataProxy object.
11168 * @param {Object} o The data object.
11169 * @param {Object} arg The callback argument object passed to the load function.
11173 * @event loadexception
11174 * Fires if an Exception occurs during data retrieval.
11175 * @param {Object} This DataProxy object.
11176 * @param {Object} o The data object.
11177 * @param {Object} arg The callback argument object passed to the load function.
11178 * @param {Object} e The Exception.
11180 loadexception : true
11182 Roo.data.DataProxy.superclass.constructor.call(this);
11185 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11188 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11192 * Ext JS Library 1.1.1
11193 * Copyright(c) 2006-2007, Ext JS, LLC.
11195 * Originally Released Under LGPL - original licence link has changed is not relivant.
11198 * <script type="text/javascript">
11201 * @class Roo.data.MemoryProxy
11202 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11203 * to the Reader when its load method is called.
11205 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11207 Roo.data.MemoryProxy = function(data){
11211 Roo.data.MemoryProxy.superclass.constructor.call(this);
11215 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11218 * Load data from the requested source (in this case an in-memory
11219 * data object passed to the constructor), read the data object into
11220 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11221 * process that block using the passed callback.
11222 * @param {Object} params This parameter is not used by the MemoryProxy class.
11223 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11224 * object into a block of Roo.data.Records.
11225 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11226 * The function must be passed <ul>
11227 * <li>The Record block object</li>
11228 * <li>The "arg" argument from the load function</li>
11229 * <li>A boolean success indicator</li>
11231 * @param {Object} scope The scope in which to call the callback
11232 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11234 load : function(params, reader, callback, scope, arg){
11235 params = params || {};
11238 result = reader.readRecords(this.data);
11240 this.fireEvent("loadexception", this, arg, null, e);
11241 callback.call(scope, null, arg, false);
11244 callback.call(scope, result, arg, true);
11248 update : function(params, records){
11253 * Ext JS Library 1.1.1
11254 * Copyright(c) 2006-2007, Ext JS, LLC.
11256 * Originally Released Under LGPL - original licence link has changed is not relivant.
11259 * <script type="text/javascript">
11262 * @class Roo.data.HttpProxy
11263 * @extends Roo.data.DataProxy
11264 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11265 * configured to reference a certain URL.<br><br>
11267 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11268 * from which the running page was served.<br><br>
11270 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11272 * Be aware that to enable the browser to parse an XML document, the server must set
11273 * the Content-Type header in the HTTP response to "text/xml".
11275 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11276 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11277 * will be used to make the request.
11279 Roo.data.HttpProxy = function(conn){
11280 Roo.data.HttpProxy.superclass.constructor.call(this);
11281 // is conn a conn config or a real conn?
11283 this.useAjax = !conn || !conn.events;
11287 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11288 // thse are take from connection...
11291 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11294 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11295 * extra parameters to each request made by this object. (defaults to undefined)
11298 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11299 * to each request made by this object. (defaults to undefined)
11302 * @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)
11305 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11308 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11314 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11318 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11319 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11320 * a finer-grained basis than the DataProxy events.
11322 getConnection : function(){
11323 return this.useAjax ? Roo.Ajax : this.conn;
11327 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11328 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11329 * process that block using the passed callback.
11330 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11331 * for the request to the remote server.
11332 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11333 * object into a block of Roo.data.Records.
11334 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11335 * The function must be passed <ul>
11336 * <li>The Record block object</li>
11337 * <li>The "arg" argument from the load function</li>
11338 * <li>A boolean success indicator</li>
11340 * @param {Object} scope The scope in which to call the callback
11341 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11343 load : function(params, reader, callback, scope, arg){
11344 if(this.fireEvent("beforeload", this, params) !== false){
11346 params : params || {},
11348 callback : callback,
11353 callback : this.loadResponse,
11357 Roo.applyIf(o, this.conn);
11358 if(this.activeRequest){
11359 Roo.Ajax.abort(this.activeRequest);
11361 this.activeRequest = Roo.Ajax.request(o);
11363 this.conn.request(o);
11366 callback.call(scope||this, null, arg, false);
11371 loadResponse : function(o, success, response){
11372 delete this.activeRequest;
11374 this.fireEvent("loadexception", this, o, response);
11375 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11380 result = o.reader.read(response);
11382 this.fireEvent("loadexception", this, o, response, e);
11383 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11387 this.fireEvent("load", this, o, o.request.arg);
11388 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11392 update : function(dataSet){
11397 updateResponse : function(dataSet){
11402 * Ext JS Library 1.1.1
11403 * Copyright(c) 2006-2007, Ext JS, LLC.
11405 * Originally Released Under LGPL - original licence link has changed is not relivant.
11408 * <script type="text/javascript">
11412 * @class Roo.data.ScriptTagProxy
11413 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11414 * other than the originating domain of the running page.<br><br>
11416 * <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
11417 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11419 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11420 * source code that is used as the source inside a <script> tag.<br><br>
11422 * In order for the browser to process the returned data, the server must wrap the data object
11423 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11424 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11425 * depending on whether the callback name was passed:
11428 boolean scriptTag = false;
11429 String cb = request.getParameter("callback");
11432 response.setContentType("text/javascript");
11434 response.setContentType("application/x-json");
11436 Writer out = response.getWriter();
11438 out.write(cb + "(");
11440 out.print(dataBlock.toJsonString());
11447 * @param {Object} config A configuration object.
11449 Roo.data.ScriptTagProxy = function(config){
11450 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11451 Roo.apply(this, config);
11452 this.head = document.getElementsByTagName("head")[0];
11455 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11457 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11459 * @cfg {String} url The URL from which to request the data object.
11462 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11466 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11467 * the server the name of the callback function set up by the load call to process the returned data object.
11468 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11469 * javascript output which calls this named function passing the data object as its only parameter.
11471 callbackParam : "callback",
11473 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11474 * name to the request.
11479 * Load data from the configured URL, read the data object into
11480 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11481 * process that block using the passed callback.
11482 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11483 * for the request to the remote server.
11484 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11485 * object into a block of Roo.data.Records.
11486 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11487 * The function must be passed <ul>
11488 * <li>The Record block object</li>
11489 * <li>The "arg" argument from the load function</li>
11490 * <li>A boolean success indicator</li>
11492 * @param {Object} scope The scope in which to call the callback
11493 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11495 load : function(params, reader, callback, scope, arg){
11496 if(this.fireEvent("beforeload", this, params) !== false){
11498 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11500 var url = this.url;
11501 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11503 url += "&_dc=" + (new Date().getTime());
11505 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11508 cb : "stcCallback"+transId,
11509 scriptId : "stcScript"+transId,
11513 callback : callback,
11519 window[trans.cb] = function(o){
11520 conn.handleResponse(o, trans);
11523 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11525 if(this.autoAbort !== false){
11529 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11531 var script = document.createElement("script");
11532 script.setAttribute("src", url);
11533 script.setAttribute("type", "text/javascript");
11534 script.setAttribute("id", trans.scriptId);
11535 this.head.appendChild(script);
11537 this.trans = trans;
11539 callback.call(scope||this, null, arg, false);
11544 isLoading : function(){
11545 return this.trans ? true : false;
11549 * Abort the current server request.
11551 abort : function(){
11552 if(this.isLoading()){
11553 this.destroyTrans(this.trans);
11558 destroyTrans : function(trans, isLoaded){
11559 this.head.removeChild(document.getElementById(trans.scriptId));
11560 clearTimeout(trans.timeoutId);
11562 window[trans.cb] = undefined;
11564 delete window[trans.cb];
11567 // if hasn't been loaded, wait for load to remove it to prevent script error
11568 window[trans.cb] = function(){
11569 window[trans.cb] = undefined;
11571 delete window[trans.cb];
11578 handleResponse : function(o, trans){
11579 this.trans = false;
11580 this.destroyTrans(trans, true);
11583 result = trans.reader.readRecords(o);
11585 this.fireEvent("loadexception", this, o, trans.arg, e);
11586 trans.callback.call(trans.scope||window, null, trans.arg, false);
11589 this.fireEvent("load", this, o, trans.arg);
11590 trans.callback.call(trans.scope||window, result, trans.arg, true);
11594 handleFailure : function(trans){
11595 this.trans = false;
11596 this.destroyTrans(trans, false);
11597 this.fireEvent("loadexception", this, null, trans.arg);
11598 trans.callback.call(trans.scope||window, null, trans.arg, false);
11602 * Ext JS Library 1.1.1
11603 * Copyright(c) 2006-2007, Ext JS, LLC.
11605 * Originally Released Under LGPL - original licence link has changed is not relivant.
11608 * <script type="text/javascript">
11612 * @class Roo.data.JsonReader
11613 * @extends Roo.data.DataReader
11614 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11615 * based on mappings in a provided Roo.data.Record constructor.
11617 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11618 * in the reply previously.
11623 var RecordDef = Roo.data.Record.create([
11624 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11625 {name: 'occupation'} // This field will use "occupation" as the mapping.
11627 var myReader = new Roo.data.JsonReader({
11628 totalProperty: "results", // The property which contains the total dataset size (optional)
11629 root: "rows", // The property which contains an Array of row objects
11630 id: "id" // The property within each row object that provides an ID for the record (optional)
11634 * This would consume a JSON file like this:
11636 { 'results': 2, 'rows': [
11637 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11638 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11641 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11642 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11643 * paged from the remote server.
11644 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11645 * @cfg {String} root name of the property which contains the Array of row objects.
11646 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11647 * @cfg {Array} fields Array of field definition objects
11649 * Create a new JsonReader
11650 * @param {Object} meta Metadata configuration options
11651 * @param {Object} recordType Either an Array of field definition objects,
11652 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11654 Roo.data.JsonReader = function(meta, recordType){
11657 // set some defaults:
11658 Roo.applyIf(meta, {
11659 totalProperty: 'total',
11660 successProperty : 'success',
11665 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11667 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11670 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11671 * Used by Store query builder to append _requestMeta to params.
11674 metaFromRemote : false,
11676 * This method is only used by a DataProxy which has retrieved data from a remote server.
11677 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11678 * @return {Object} data A data block which is used by an Roo.data.Store object as
11679 * a cache of Roo.data.Records.
11681 read : function(response){
11682 var json = response.responseText;
11684 var o = /* eval:var:o */ eval("("+json+")");
11686 throw {message: "JsonReader.read: Json object not found"};
11692 this.metaFromRemote = true;
11693 this.meta = o.metaData;
11694 this.recordType = Roo.data.Record.create(o.metaData.fields);
11695 this.onMetaChange(this.meta, this.recordType, o);
11697 return this.readRecords(o);
11700 // private function a store will implement
11701 onMetaChange : function(meta, recordType, o){
11708 simpleAccess: function(obj, subsc) {
11715 getJsonAccessor: function(){
11717 return function(expr) {
11719 return(re.test(expr))
11720 ? new Function("obj", "return obj." + expr)
11725 return Roo.emptyFn;
11730 * Create a data block containing Roo.data.Records from an XML document.
11731 * @param {Object} o An object which contains an Array of row objects in the property specified
11732 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11733 * which contains the total size of the dataset.
11734 * @return {Object} data A data block which is used by an Roo.data.Store object as
11735 * a cache of Roo.data.Records.
11737 readRecords : function(o){
11739 * After any data loads, the raw JSON data is available for further custom processing.
11743 var s = this.meta, Record = this.recordType,
11744 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11746 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11748 if(s.totalProperty) {
11749 this.getTotal = this.getJsonAccessor(s.totalProperty);
11751 if(s.successProperty) {
11752 this.getSuccess = this.getJsonAccessor(s.successProperty);
11754 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11756 var g = this.getJsonAccessor(s.id);
11757 this.getId = function(rec) {
11759 return (r === undefined || r === "") ? null : r;
11762 this.getId = function(){return null;};
11765 for(var jj = 0; jj < fl; jj++){
11767 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11768 this.ef[jj] = this.getJsonAccessor(map);
11772 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11773 if(s.totalProperty){
11774 var vt = parseInt(this.getTotal(o), 10);
11779 if(s.successProperty){
11780 var vs = this.getSuccess(o);
11781 if(vs === false || vs === 'false'){
11786 for(var i = 0; i < c; i++){
11789 var id = this.getId(n);
11790 for(var j = 0; j < fl; j++){
11792 var v = this.ef[j](n);
11794 Roo.log('missing convert for ' + f.name);
11798 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11800 var record = new Record(values, id);
11802 records[i] = record;
11808 totalRecords : totalRecords
11813 * Ext JS Library 1.1.1
11814 * Copyright(c) 2006-2007, Ext JS, LLC.
11816 * Originally Released Under LGPL - original licence link has changed is not relivant.
11819 * <script type="text/javascript">
11823 * @class Roo.data.ArrayReader
11824 * @extends Roo.data.DataReader
11825 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11826 * Each element of that Array represents a row of data fields. The
11827 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11828 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11832 var RecordDef = Roo.data.Record.create([
11833 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11834 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11836 var myReader = new Roo.data.ArrayReader({
11837 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11841 * This would consume an Array like this:
11843 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11845 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11847 * Create a new JsonReader
11848 * @param {Object} meta Metadata configuration options.
11849 * @param {Object} recordType Either an Array of field definition objects
11850 * as specified to {@link Roo.data.Record#create},
11851 * or an {@link Roo.data.Record} object
11852 * created using {@link Roo.data.Record#create}.
11854 Roo.data.ArrayReader = function(meta, recordType){
11855 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11858 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11860 * Create a data block containing Roo.data.Records from an XML document.
11861 * @param {Object} o An Array of row objects which represents the dataset.
11862 * @return {Object} data A data block which is used by an Roo.data.Store object as
11863 * a cache of Roo.data.Records.
11865 readRecords : function(o){
11866 var sid = this.meta ? this.meta.id : null;
11867 var recordType = this.recordType, fields = recordType.prototype.fields;
11870 for(var i = 0; i < root.length; i++){
11873 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11874 for(var j = 0, jlen = fields.length; j < jlen; j++){
11875 var f = fields.items[j];
11876 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11877 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11879 values[f.name] = v;
11881 var record = new recordType(values, id);
11883 records[records.length] = record;
11887 totalRecords : records.length
11896 * @class Roo.bootstrap.ComboBox
11897 * @extends Roo.bootstrap.TriggerField
11898 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11899 * @cfg {Boolean} append (true|false) default false
11900 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11901 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11902 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11903 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11904 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11905 * @cfg {Boolean} animate default true
11906 * @cfg {Boolean} emptyResultText only for touch device
11907 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11909 * Create a new ComboBox.
11910 * @param {Object} config Configuration options
11912 Roo.bootstrap.ComboBox = function(config){
11913 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11917 * Fires when the dropdown list is expanded
11918 * @param {Roo.bootstrap.ComboBox} combo This combo box
11923 * Fires when the dropdown list is collapsed
11924 * @param {Roo.bootstrap.ComboBox} combo This combo box
11928 * @event beforeselect
11929 * Fires before a list item is selected. Return false to cancel the selection.
11930 * @param {Roo.bootstrap.ComboBox} combo This combo box
11931 * @param {Roo.data.Record} record The data record returned from the underlying store
11932 * @param {Number} index The index of the selected item in the dropdown list
11934 'beforeselect' : true,
11937 * Fires when a list item is selected
11938 * @param {Roo.bootstrap.ComboBox} combo This combo box
11939 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11940 * @param {Number} index The index of the selected item in the dropdown list
11944 * @event beforequery
11945 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11946 * The event object passed has these properties:
11947 * @param {Roo.bootstrap.ComboBox} combo This combo box
11948 * @param {String} query The query
11949 * @param {Boolean} forceAll true to force "all" query
11950 * @param {Boolean} cancel true to cancel the query
11951 * @param {Object} e The query event object
11953 'beforequery': true,
11956 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11957 * @param {Roo.bootstrap.ComboBox} combo This combo box
11962 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11963 * @param {Roo.bootstrap.ComboBox} combo This combo box
11964 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11969 * Fires when the remove value from the combobox array
11970 * @param {Roo.bootstrap.ComboBox} combo This combo box
11974 * @event afterremove
11975 * Fires when the remove value from the combobox array
11976 * @param {Roo.bootstrap.ComboBox} combo This combo box
11978 'afterremove' : true,
11980 * @event specialfilter
11981 * Fires when specialfilter
11982 * @param {Roo.bootstrap.ComboBox} combo This combo box
11984 'specialfilter' : true,
11987 * Fires when tick the element
11988 * @param {Roo.bootstrap.ComboBox} combo This combo box
11992 * @event touchviewdisplay
11993 * Fires when touch view require special display (default is using displayField)
11994 * @param {Roo.bootstrap.ComboBox} combo This combo box
11995 * @param {Object} cfg set html .
11997 'touchviewdisplay' : true
12002 this.tickItems = [];
12004 this.selectedIndex = -1;
12005 if(this.mode == 'local'){
12006 if(config.queryDelay === undefined){
12007 this.queryDelay = 10;
12009 if(config.minChars === undefined){
12015 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12018 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12019 * rendering into an Roo.Editor, defaults to false)
12022 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12023 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12026 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12029 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12030 * the dropdown list (defaults to undefined, with no header element)
12034 * @cfg {String/Roo.Template} tpl The template to use to render the output
12038 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12040 listWidth: undefined,
12042 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12043 * mode = 'remote' or 'text' if mode = 'local')
12045 displayField: undefined,
12048 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12049 * mode = 'remote' or 'value' if mode = 'local').
12050 * Note: use of a valueField requires the user make a selection
12051 * in order for a value to be mapped.
12053 valueField: undefined,
12055 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12060 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12061 * field's data value (defaults to the underlying DOM element's name)
12063 hiddenName: undefined,
12065 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12069 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12071 selectedClass: 'active',
12074 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12078 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12079 * anchor positions (defaults to 'tl-bl')
12081 listAlign: 'tl-bl?',
12083 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12087 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12088 * query specified by the allQuery config option (defaults to 'query')
12090 triggerAction: 'query',
12092 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12093 * (defaults to 4, does not apply if editable = false)
12097 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12098 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12102 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12103 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12107 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12108 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12112 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12113 * when editable = true (defaults to false)
12115 selectOnFocus:false,
12117 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12119 queryParam: 'query',
12121 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12122 * when mode = 'remote' (defaults to 'Loading...')
12124 loadingText: 'Loading...',
12126 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12130 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12134 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12135 * traditional select (defaults to true)
12139 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12143 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12147 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12148 * listWidth has a higher value)
12152 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12153 * allow the user to set arbitrary text into the field (defaults to false)
12155 forceSelection:false,
12157 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12158 * if typeAhead = true (defaults to 250)
12160 typeAheadDelay : 250,
12162 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12163 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12165 valueNotFoundText : undefined,
12167 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12169 blockFocus : false,
12172 * @cfg {Boolean} disableClear Disable showing of clear button.
12174 disableClear : false,
12176 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12178 alwaysQuery : false,
12181 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12186 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12188 invalidClass : "has-warning",
12191 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12193 validClass : "has-success",
12196 * @cfg {Boolean} specialFilter (true|false) special filter default false
12198 specialFilter : false,
12201 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12203 mobileTouchView : true,
12215 btnPosition : 'right',
12216 triggerList : true,
12217 showToggleBtn : true,
12219 emptyResultText: 'Empty',
12220 triggerText : 'Select',
12222 // element that contains real text value.. (when hidden is used..)
12224 getAutoCreate : function()
12232 if(Roo.isTouch && this.mobileTouchView){
12233 cfg = this.getAutoCreateTouchView();
12240 if(!this.tickable){
12241 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12246 * ComboBox with tickable selections
12249 var align = this.labelAlign || this.parentLabelAlign();
12252 cls : 'form-group roo-combobox-tickable' //input-group
12257 cls : 'tickable-buttons',
12262 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12263 html : this.triggerText
12269 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12276 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12283 buttons.cn.unshift({
12285 cls: 'roo-select2-search-field-input'
12291 Roo.each(buttons.cn, function(c){
12293 c.cls += ' btn-' + _this.size;
12296 if (_this.disabled) {
12307 cls: 'form-hidden-field'
12311 cls: 'roo-select2-choices',
12315 cls: 'roo-select2-search-field',
12327 cls: 'roo-select2-container input-group roo-select2-container-multi',
12332 // cls: 'typeahead typeahead-long dropdown-menu',
12333 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12338 if(this.hasFeedback && !this.allowBlank){
12342 cls: 'glyphicon form-control-feedback'
12345 combobox.cn.push(feedback);
12348 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12350 // Roo.log("left and has label");
12354 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12355 tooltip : 'This field is required'
12360 cls : 'control-label col-sm-' + this.labelWidth,
12361 html : this.fieldLabel
12365 cls : "col-sm-" + (12 - this.labelWidth),
12373 if(this.indicatorpos == 'right'){
12379 cls : 'control-label col-sm-' + this.labelWidth,
12380 html : this.fieldLabel
12385 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12386 tooltip : 'This field is required'
12389 cls : "col-sm-" + (12 - this.labelWidth),
12400 } else if ( this.fieldLabel.length) {
12401 // Roo.log(" label");
12405 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12406 tooltip : 'This field is required'
12410 //cls : 'input-group-addon',
12411 html : this.fieldLabel
12419 if(this.indicatorpos == 'right'){
12424 //cls : 'input-group-addon',
12425 html : this.fieldLabel
12431 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12432 tooltip : 'This field is required'
12443 // Roo.log(" no label && no align");
12450 ['xs','sm','md','lg'].map(function(size){
12451 if (settings[size]) {
12452 cfg.cls += ' col-' + size + '-' + settings[size];
12460 _initEventsCalled : false,
12463 initEvents: function()
12466 if (this._initEventsCalled) { // as we call render... prevent looping...
12469 this._initEventsCalled = true;
12472 throw "can not find store for combo";
12475 this.store = Roo.factory(this.store, Roo.data);
12477 // if we are building from html. then this element is so complex, that we can not really
12478 // use the rendered HTML.
12479 // so we have to trash and replace the previous code.
12480 if (Roo.XComponent.build_from_html) {
12482 // remove this element....
12483 var e = this.el.dom, k=0;
12484 while (e ) { e = e.previousSibling; ++k;}
12489 this.rendered = false;
12491 this.render(this.parent().getChildContainer(true), k);
12502 if(Roo.isTouch && this.mobileTouchView){
12503 this.initTouchView();
12508 this.initTickableEvents();
12512 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12514 if(this.hiddenName){
12516 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12518 this.hiddenField.dom.value =
12519 this.hiddenValue !== undefined ? this.hiddenValue :
12520 this.value !== undefined ? this.value : '';
12522 // prevent input submission
12523 this.el.dom.removeAttribute('name');
12524 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12529 // this.el.dom.setAttribute('autocomplete', 'off');
12532 var cls = 'x-combo-list';
12534 //this.list = new Roo.Layer({
12535 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12541 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12542 _this.list.setWidth(lw);
12545 this.list.on('mouseover', this.onViewOver, this);
12546 this.list.on('mousemove', this.onViewMove, this);
12548 this.list.on('scroll', this.onViewScroll, this);
12551 this.list.swallowEvent('mousewheel');
12552 this.assetHeight = 0;
12555 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12556 this.assetHeight += this.header.getHeight();
12559 this.innerList = this.list.createChild({cls:cls+'-inner'});
12560 this.innerList.on('mouseover', this.onViewOver, this);
12561 this.innerList.on('mousemove', this.onViewMove, this);
12562 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12564 if(this.allowBlank && !this.pageSize && !this.disableClear){
12565 this.footer = this.list.createChild({cls:cls+'-ft'});
12566 this.pageTb = new Roo.Toolbar(this.footer);
12570 this.footer = this.list.createChild({cls:cls+'-ft'});
12571 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12572 {pageSize: this.pageSize});
12576 if (this.pageTb && this.allowBlank && !this.disableClear) {
12578 this.pageTb.add(new Roo.Toolbar.Fill(), {
12579 cls: 'x-btn-icon x-btn-clear',
12581 handler: function()
12584 _this.clearValue();
12585 _this.onSelect(false, -1);
12590 this.assetHeight += this.footer.getHeight();
12595 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12598 this.view = new Roo.View(this.list, this.tpl, {
12599 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12601 //this.view.wrapEl.setDisplayed(false);
12602 this.view.on('click', this.onViewClick, this);
12606 this.store.on('beforeload', this.onBeforeLoad, this);
12607 this.store.on('load', this.onLoad, this);
12608 this.store.on('loadexception', this.onLoadException, this);
12610 if(this.resizable){
12611 this.resizer = new Roo.Resizable(this.list, {
12612 pinned:true, handles:'se'
12614 this.resizer.on('resize', function(r, w, h){
12615 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12616 this.listWidth = w;
12617 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12618 this.restrictHeight();
12620 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12623 if(!this.editable){
12624 this.editable = true;
12625 this.setEditable(false);
12630 if (typeof(this.events.add.listeners) != 'undefined') {
12632 this.addicon = this.wrap.createChild(
12633 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12635 this.addicon.on('click', function(e) {
12636 this.fireEvent('add', this);
12639 if (typeof(this.events.edit.listeners) != 'undefined') {
12641 this.editicon = this.wrap.createChild(
12642 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12643 if (this.addicon) {
12644 this.editicon.setStyle('margin-left', '40px');
12646 this.editicon.on('click', function(e) {
12648 // we fire even if inothing is selected..
12649 this.fireEvent('edit', this, this.lastData );
12655 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12656 "up" : function(e){
12657 this.inKeyMode = true;
12661 "down" : function(e){
12662 if(!this.isExpanded()){
12663 this.onTriggerClick();
12665 this.inKeyMode = true;
12670 "enter" : function(e){
12671 // this.onViewClick();
12675 if(this.fireEvent("specialkey", this, e)){
12676 this.onViewClick(false);
12682 "esc" : function(e){
12686 "tab" : function(e){
12689 if(this.fireEvent("specialkey", this, e)){
12690 this.onViewClick(false);
12698 doRelay : function(foo, bar, hname){
12699 if(hname == 'down' || this.scope.isExpanded()){
12700 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12709 this.queryDelay = Math.max(this.queryDelay || 10,
12710 this.mode == 'local' ? 10 : 250);
12713 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12715 if(this.typeAhead){
12716 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12718 if(this.editable !== false){
12719 this.inputEl().on("keyup", this.onKeyUp, this);
12721 if(this.forceSelection){
12722 this.inputEl().on('blur', this.doForce, this);
12726 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12727 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12731 initTickableEvents: function()
12735 if(this.hiddenName){
12737 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12739 this.hiddenField.dom.value =
12740 this.hiddenValue !== undefined ? this.hiddenValue :
12741 this.value !== undefined ? this.value : '';
12743 // prevent input submission
12744 this.el.dom.removeAttribute('name');
12745 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12750 // this.list = this.el.select('ul.dropdown-menu',true).first();
12752 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12753 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12754 if(this.triggerList){
12755 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12758 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12759 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12761 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12762 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12764 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12765 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12767 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12768 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12769 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12772 this.cancelBtn.hide();
12777 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12778 _this.list.setWidth(lw);
12781 this.list.on('mouseover', this.onViewOver, this);
12782 this.list.on('mousemove', this.onViewMove, this);
12784 this.list.on('scroll', this.onViewScroll, this);
12787 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>';
12790 this.view = new Roo.View(this.list, this.tpl, {
12791 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12794 //this.view.wrapEl.setDisplayed(false);
12795 this.view.on('click', this.onViewClick, this);
12799 this.store.on('beforeload', this.onBeforeLoad, this);
12800 this.store.on('load', this.onLoad, this);
12801 this.store.on('loadexception', this.onLoadException, this);
12804 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12805 "up" : function(e){
12806 this.inKeyMode = true;
12810 "down" : function(e){
12811 this.inKeyMode = true;
12815 "enter" : function(e){
12816 if(this.fireEvent("specialkey", this, e)){
12817 this.onViewClick(false);
12823 "esc" : function(e){
12824 this.onTickableFooterButtonClick(e, false, false);
12827 "tab" : function(e){
12828 this.fireEvent("specialkey", this, e);
12830 this.onTickableFooterButtonClick(e, false, false);
12837 doRelay : function(e, fn, key){
12838 if(this.scope.isExpanded()){
12839 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12848 this.queryDelay = Math.max(this.queryDelay || 10,
12849 this.mode == 'local' ? 10 : 250);
12852 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12854 if(this.typeAhead){
12855 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12858 if(this.editable !== false){
12859 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12864 onDestroy : function(){
12866 this.view.setStore(null);
12867 this.view.el.removeAllListeners();
12868 this.view.el.remove();
12869 this.view.purgeListeners();
12872 this.list.dom.innerHTML = '';
12876 this.store.un('beforeload', this.onBeforeLoad, this);
12877 this.store.un('load', this.onLoad, this);
12878 this.store.un('loadexception', this.onLoadException, this);
12880 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12884 fireKey : function(e){
12885 if(e.isNavKeyPress() && !this.list.isVisible()){
12886 this.fireEvent("specialkey", this, e);
12891 onResize: function(w, h){
12892 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12894 // if(typeof w != 'number'){
12895 // // we do not handle it!?!?
12898 // var tw = this.trigger.getWidth();
12899 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12900 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12902 // this.inputEl().setWidth( this.adjustWidth('input', x));
12904 // //this.trigger.setStyle('left', x+'px');
12906 // if(this.list && this.listWidth === undefined){
12907 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12908 // this.list.setWidth(lw);
12909 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12917 * Allow or prevent the user from directly editing the field text. If false is passed,
12918 * the user will only be able to select from the items defined in the dropdown list. This method
12919 * is the runtime equivalent of setting the 'editable' config option at config time.
12920 * @param {Boolean} value True to allow the user to directly edit the field text
12922 setEditable : function(value){
12923 if(value == this.editable){
12926 this.editable = value;
12928 this.inputEl().dom.setAttribute('readOnly', true);
12929 this.inputEl().on('mousedown', this.onTriggerClick, this);
12930 this.inputEl().addClass('x-combo-noedit');
12932 this.inputEl().dom.setAttribute('readOnly', false);
12933 this.inputEl().un('mousedown', this.onTriggerClick, this);
12934 this.inputEl().removeClass('x-combo-noedit');
12940 onBeforeLoad : function(combo,opts){
12941 if(!this.hasFocus){
12945 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12947 this.restrictHeight();
12948 this.selectedIndex = -1;
12952 onLoad : function(){
12954 this.hasQuery = false;
12956 if(!this.hasFocus){
12960 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12961 this.loading.hide();
12964 if(this.store.getCount() > 0){
12966 this.restrictHeight();
12967 if(this.lastQuery == this.allQuery){
12968 if(this.editable && !this.tickable){
12969 this.inputEl().dom.select();
12973 !this.selectByValue(this.value, true) &&
12976 !this.store.lastOptions ||
12977 typeof(this.store.lastOptions.add) == 'undefined' ||
12978 this.store.lastOptions.add != true
12981 this.select(0, true);
12984 if(this.autoFocus){
12987 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12988 this.taTask.delay(this.typeAheadDelay);
12992 this.onEmptyResults();
12998 onLoadException : function()
13000 this.hasQuery = false;
13002 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13003 this.loading.hide();
13006 if(this.tickable && this.editable){
13011 // only causes errors at present
13012 //Roo.log(this.store.reader.jsonData);
13013 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13015 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13021 onTypeAhead : function(){
13022 if(this.store.getCount() > 0){
13023 var r = this.store.getAt(0);
13024 var newValue = r.data[this.displayField];
13025 var len = newValue.length;
13026 var selStart = this.getRawValue().length;
13028 if(selStart != len){
13029 this.setRawValue(newValue);
13030 this.selectText(selStart, newValue.length);
13036 onSelect : function(record, index){
13038 if(this.fireEvent('beforeselect', this, record, index) !== false){
13040 this.setFromData(index > -1 ? record.data : false);
13043 this.fireEvent('select', this, record, index);
13048 * Returns the currently selected field value or empty string if no value is set.
13049 * @return {String} value The selected value
13051 getValue : function(){
13054 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13057 if(this.valueField){
13058 return typeof this.value != 'undefined' ? this.value : '';
13060 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13065 * Clears any text/value currently set in the field
13067 clearValue : function(){
13068 if(this.hiddenField){
13069 this.hiddenField.dom.value = '';
13072 this.setRawValue('');
13073 this.lastSelectionText = '';
13074 this.lastData = false;
13076 var close = this.closeTriggerEl();
13087 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13088 * will be displayed in the field. If the value does not match the data value of an existing item,
13089 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13090 * Otherwise the field will be blank (although the value will still be set).
13091 * @param {String} value The value to match
13093 setValue : function(v){
13100 if(this.valueField){
13101 var r = this.findRecord(this.valueField, v);
13103 text = r.data[this.displayField];
13104 }else if(this.valueNotFoundText !== undefined){
13105 text = this.valueNotFoundText;
13108 this.lastSelectionText = text;
13109 if(this.hiddenField){
13110 this.hiddenField.dom.value = v;
13112 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13115 var close = this.closeTriggerEl();
13118 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13124 * @property {Object} the last set data for the element
13129 * Sets the value of the field based on a object which is related to the record format for the store.
13130 * @param {Object} value the value to set as. or false on reset?
13132 setFromData : function(o){
13139 var dv = ''; // display value
13140 var vv = ''; // value value..
13142 if (this.displayField) {
13143 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13145 // this is an error condition!!!
13146 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13149 if(this.valueField){
13150 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13153 var close = this.closeTriggerEl();
13156 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13159 if(this.hiddenField){
13160 this.hiddenField.dom.value = vv;
13162 this.lastSelectionText = dv;
13163 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13167 // no hidden field.. - we store the value in 'value', but still display
13168 // display field!!!!
13169 this.lastSelectionText = dv;
13170 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13177 reset : function(){
13178 // overridden so that last data is reset..
13185 this.setValue(this.originalValue);
13186 //this.clearInvalid();
13187 this.lastData = false;
13189 this.view.clearSelections();
13195 findRecord : function(prop, value){
13197 if(this.store.getCount() > 0){
13198 this.store.each(function(r){
13199 if(r.data[prop] == value){
13209 getName: function()
13211 // returns hidden if it's set..
13212 if (!this.rendered) {return ''};
13213 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13217 onViewMove : function(e, t){
13218 this.inKeyMode = false;
13222 onViewOver : function(e, t){
13223 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13226 var item = this.view.findItemFromChild(t);
13229 var index = this.view.indexOf(item);
13230 this.select(index, false);
13235 onViewClick : function(view, doFocus, el, e)
13237 var index = this.view.getSelectedIndexes()[0];
13239 var r = this.store.getAt(index);
13243 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13250 Roo.each(this.tickItems, function(v,k){
13252 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13254 _this.tickItems.splice(k, 1);
13256 if(typeof(e) == 'undefined' && view == false){
13257 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13269 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13270 this.tickItems.push(r.data);
13273 if(typeof(e) == 'undefined' && view == false){
13274 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13281 this.onSelect(r, index);
13283 if(doFocus !== false && !this.blockFocus){
13284 this.inputEl().focus();
13289 restrictHeight : function(){
13290 //this.innerList.dom.style.height = '';
13291 //var inner = this.innerList.dom;
13292 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13293 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13294 //this.list.beginUpdate();
13295 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13296 this.list.alignTo(this.inputEl(), this.listAlign);
13297 this.list.alignTo(this.inputEl(), this.listAlign);
13298 //this.list.endUpdate();
13302 onEmptyResults : function(){
13304 if(this.tickable && this.editable){
13305 this.restrictHeight();
13313 * Returns true if the dropdown list is expanded, else false.
13315 isExpanded : function(){
13316 return this.list.isVisible();
13320 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13321 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13322 * @param {String} value The data value of the item to select
13323 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13324 * selected item if it is not currently in view (defaults to true)
13325 * @return {Boolean} True if the value matched an item in the list, else false
13327 selectByValue : function(v, scrollIntoView){
13328 if(v !== undefined && v !== null){
13329 var r = this.findRecord(this.valueField || this.displayField, v);
13331 this.select(this.store.indexOf(r), scrollIntoView);
13339 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13340 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13341 * @param {Number} index The zero-based index of the list item to select
13342 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13343 * selected item if it is not currently in view (defaults to true)
13345 select : function(index, scrollIntoView){
13346 this.selectedIndex = index;
13347 this.view.select(index);
13348 if(scrollIntoView !== false){
13349 var el = this.view.getNode(index);
13351 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13354 this.list.scrollChildIntoView(el, false);
13360 selectNext : function(){
13361 var ct = this.store.getCount();
13363 if(this.selectedIndex == -1){
13365 }else if(this.selectedIndex < ct-1){
13366 this.select(this.selectedIndex+1);
13372 selectPrev : function(){
13373 var ct = this.store.getCount();
13375 if(this.selectedIndex == -1){
13377 }else if(this.selectedIndex != 0){
13378 this.select(this.selectedIndex-1);
13384 onKeyUp : function(e){
13385 if(this.editable !== false && !e.isSpecialKey()){
13386 this.lastKey = e.getKey();
13387 this.dqTask.delay(this.queryDelay);
13392 validateBlur : function(){
13393 return !this.list || !this.list.isVisible();
13397 initQuery : function(){
13399 var v = this.getRawValue();
13401 if(this.tickable && this.editable){
13402 v = this.tickableInputEl().getValue();
13409 doForce : function(){
13410 if(this.inputEl().dom.value.length > 0){
13411 this.inputEl().dom.value =
13412 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13418 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13419 * query allowing the query action to be canceled if needed.
13420 * @param {String} query The SQL query to execute
13421 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13422 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13423 * saved in the current store (defaults to false)
13425 doQuery : function(q, forceAll){
13427 if(q === undefined || q === null){
13432 forceAll: forceAll,
13436 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13441 forceAll = qe.forceAll;
13442 if(forceAll === true || (q.length >= this.minChars)){
13444 this.hasQuery = true;
13446 if(this.lastQuery != q || this.alwaysQuery){
13447 this.lastQuery = q;
13448 if(this.mode == 'local'){
13449 this.selectedIndex = -1;
13451 this.store.clearFilter();
13454 if(this.specialFilter){
13455 this.fireEvent('specialfilter', this);
13460 this.store.filter(this.displayField, q);
13463 this.store.fireEvent("datachanged", this.store);
13470 this.store.baseParams[this.queryParam] = q;
13472 var options = {params : this.getParams(q)};
13475 options.add = true;
13476 options.params.start = this.page * this.pageSize;
13479 this.store.load(options);
13482 * this code will make the page width larger, at the beginning, the list not align correctly,
13483 * we should expand the list on onLoad
13484 * so command out it
13489 this.selectedIndex = -1;
13494 this.loadNext = false;
13498 getParams : function(q){
13500 //p[this.queryParam] = q;
13504 p.limit = this.pageSize;
13510 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13512 collapse : function(){
13513 if(!this.isExpanded()){
13520 this.hasFocus = false;
13522 this.cancelBtn.hide();
13523 this.trigger.show();
13526 this.tickableInputEl().dom.value = '';
13527 this.tickableInputEl().blur();
13532 Roo.get(document).un('mousedown', this.collapseIf, this);
13533 Roo.get(document).un('mousewheel', this.collapseIf, this);
13534 if (!this.editable) {
13535 Roo.get(document).un('keydown', this.listKeyPress, this);
13537 this.fireEvent('collapse', this);
13543 collapseIf : function(e){
13544 var in_combo = e.within(this.el);
13545 var in_list = e.within(this.list);
13546 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13548 if (in_combo || in_list || is_list) {
13549 //e.stopPropagation();
13554 this.onTickableFooterButtonClick(e, false, false);
13562 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13564 expand : function(){
13566 if(this.isExpanded() || !this.hasFocus){
13570 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13571 this.list.setWidth(lw);
13578 this.restrictHeight();
13582 this.tickItems = Roo.apply([], this.item);
13585 this.cancelBtn.show();
13586 this.trigger.hide();
13589 this.tickableInputEl().focus();
13594 Roo.get(document).on('mousedown', this.collapseIf, this);
13595 Roo.get(document).on('mousewheel', this.collapseIf, this);
13596 if (!this.editable) {
13597 Roo.get(document).on('keydown', this.listKeyPress, this);
13600 this.fireEvent('expand', this);
13604 // Implements the default empty TriggerField.onTriggerClick function
13605 onTriggerClick : function(e)
13607 Roo.log('trigger click');
13609 if(this.disabled || !this.triggerList){
13614 this.loadNext = false;
13616 if(this.isExpanded()){
13618 if (!this.blockFocus) {
13619 this.inputEl().focus();
13623 this.hasFocus = true;
13624 if(this.triggerAction == 'all') {
13625 this.doQuery(this.allQuery, true);
13627 this.doQuery(this.getRawValue());
13629 if (!this.blockFocus) {
13630 this.inputEl().focus();
13635 onTickableTriggerClick : function(e)
13642 this.loadNext = false;
13643 this.hasFocus = true;
13645 if(this.triggerAction == 'all') {
13646 this.doQuery(this.allQuery, true);
13648 this.doQuery(this.getRawValue());
13652 onSearchFieldClick : function(e)
13654 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13655 this.onTickableFooterButtonClick(e, false, false);
13659 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13664 this.loadNext = false;
13665 this.hasFocus = true;
13667 if(this.triggerAction == 'all') {
13668 this.doQuery(this.allQuery, true);
13670 this.doQuery(this.getRawValue());
13674 listKeyPress : function(e)
13676 //Roo.log('listkeypress');
13677 // scroll to first matching element based on key pres..
13678 if (e.isSpecialKey()) {
13681 var k = String.fromCharCode(e.getKey()).toUpperCase();
13684 var csel = this.view.getSelectedNodes();
13685 var cselitem = false;
13687 var ix = this.view.indexOf(csel[0]);
13688 cselitem = this.store.getAt(ix);
13689 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13695 this.store.each(function(v) {
13697 // start at existing selection.
13698 if (cselitem.id == v.id) {
13704 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13705 match = this.store.indexOf(v);
13711 if (match === false) {
13712 return true; // no more action?
13715 this.view.select(match);
13716 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13717 sn.scrollIntoView(sn.dom.parentNode, false);
13720 onViewScroll : function(e, t){
13722 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){
13726 this.hasQuery = true;
13728 this.loading = this.list.select('.loading', true).first();
13730 if(this.loading === null){
13731 this.list.createChild({
13733 cls: 'loading roo-select2-more-results roo-select2-active',
13734 html: 'Loading more results...'
13737 this.loading = this.list.select('.loading', true).first();
13739 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13741 this.loading.hide();
13744 this.loading.show();
13749 this.loadNext = true;
13751 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13756 addItem : function(o)
13758 var dv = ''; // display value
13760 if (this.displayField) {
13761 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13763 // this is an error condition!!!
13764 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13771 var choice = this.choices.createChild({
13773 cls: 'roo-select2-search-choice',
13782 cls: 'roo-select2-search-choice-close',
13787 }, this.searchField);
13789 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13791 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13799 this.inputEl().dom.value = '';
13804 onRemoveItem : function(e, _self, o)
13806 e.preventDefault();
13808 this.lastItem = Roo.apply([], this.item);
13810 var index = this.item.indexOf(o.data) * 1;
13813 Roo.log('not this item?!');
13817 this.item.splice(index, 1);
13822 this.fireEvent('remove', this, e);
13828 syncValue : function()
13830 if(!this.item.length){
13837 Roo.each(this.item, function(i){
13838 if(_this.valueField){
13839 value.push(i[_this.valueField]);
13846 this.value = value.join(',');
13848 if(this.hiddenField){
13849 this.hiddenField.dom.value = this.value;
13852 this.store.fireEvent("datachanged", this.store);
13857 clearItem : function()
13859 if(!this.multiple){
13865 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13873 if(this.tickable && !Roo.isTouch){
13874 this.view.refresh();
13878 inputEl: function ()
13880 if(Roo.isTouch && this.mobileTouchView){
13881 return this.el.select('input.form-control',true).first();
13885 return this.searchField;
13888 return this.el.select('input.form-control',true).first();
13892 onTickableFooterButtonClick : function(e, btn, el)
13894 e.preventDefault();
13896 this.lastItem = Roo.apply([], this.item);
13898 if(btn && btn.name == 'cancel'){
13899 this.tickItems = Roo.apply([], this.item);
13908 Roo.each(this.tickItems, function(o){
13916 validate : function()
13918 var v = this.getRawValue();
13921 v = this.getValue();
13924 if(this.disabled || this.allowBlank || v.length){
13929 this.markInvalid();
13933 tickableInputEl : function()
13935 if(!this.tickable || !this.editable){
13936 return this.inputEl();
13939 return this.inputEl().select('.roo-select2-search-field-input', true).first();
13943 getAutoCreateTouchView : function()
13948 cls: 'form-group' //input-group
13954 type : this.inputType,
13955 cls : 'form-control x-combo-noedit',
13956 autocomplete: 'new-password',
13957 placeholder : this.placeholder || '',
13962 input.name = this.name;
13966 input.cls += ' input-' + this.size;
13969 if (this.disabled) {
13970 input.disabled = true;
13981 inputblock.cls += ' input-group';
13983 inputblock.cn.unshift({
13985 cls : 'input-group-addon',
13990 if(this.removable && !this.multiple){
13991 inputblock.cls += ' roo-removable';
13993 inputblock.cn.push({
13996 cls : 'roo-combo-removable-btn close'
14000 if(this.hasFeedback && !this.allowBlank){
14002 inputblock.cls += ' has-feedback';
14004 inputblock.cn.push({
14006 cls: 'glyphicon form-control-feedback'
14013 inputblock.cls += (this.before) ? '' : ' input-group';
14015 inputblock.cn.push({
14017 cls : 'input-group-addon',
14028 cls: 'form-hidden-field'
14042 cls: 'form-hidden-field'
14046 cls: 'roo-select2-choices',
14050 cls: 'roo-select2-search-field',
14063 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14069 if(!this.multiple && this.showToggleBtn){
14076 if (this.caret != false) {
14079 cls: 'fa fa-' + this.caret
14086 cls : 'input-group-addon btn dropdown-toggle',
14091 cls: 'combobox-clear',
14105 combobox.cls += ' roo-select2-container-multi';
14108 var align = this.labelAlign || this.parentLabelAlign();
14112 if(this.fieldLabel.length && this.labelWidth){
14114 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14115 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14120 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14121 tooltip : 'This field is required'
14125 cls : 'control-label ' + lw,
14126 html : this.fieldLabel
14137 if(this.indicatorpos == 'right'){
14141 cls : 'control-label ' + lw,
14142 html : this.fieldLabel
14147 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14148 tooltip : 'This field is required'
14160 var settings = this;
14162 ['xs','sm','md','lg'].map(function(size){
14163 if (settings[size]) {
14164 cfg.cls += ' col-' + size + '-' + settings[size];
14171 initTouchView : function()
14173 this.renderTouchView();
14175 this.touchViewEl.on('scroll', function(){
14176 this.el.dom.scrollTop = 0;
14179 this.originalValue = this.getValue();
14181 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14183 this.inputEl().on("click", this.showTouchView, this);
14184 this.triggerEl.on("click", this.showTouchView, this);
14186 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14187 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14189 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14191 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14192 this.store.on('load', this.onTouchViewLoad, this);
14193 this.store.on('loadexception', this.onTouchViewLoadException, this);
14195 if(this.hiddenName){
14197 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14199 this.hiddenField.dom.value =
14200 this.hiddenValue !== undefined ? this.hiddenValue :
14201 this.value !== undefined ? this.value : '';
14203 this.el.dom.removeAttribute('name');
14204 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14208 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14209 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14212 if(this.removable && !this.multiple){
14213 var close = this.closeTriggerEl();
14215 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14216 close.on('click', this.removeBtnClick, this, close);
14220 * fix the bug in Safari iOS8
14222 this.inputEl().on("focus", function(e){
14223 document.activeElement.blur();
14231 renderTouchView : function()
14233 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14234 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14236 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14237 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14239 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14240 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14241 this.touchViewBodyEl.setStyle('overflow', 'auto');
14243 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14244 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14246 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14247 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14251 showTouchView : function()
14257 this.touchViewHeaderEl.hide();
14259 if(this.modalTitle.length){
14260 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14261 this.touchViewHeaderEl.show();
14264 this.touchViewEl.show();
14266 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14267 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14268 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14270 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14272 if(this.modalTitle.length){
14273 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14276 this.touchViewBodyEl.setHeight(bodyHeight);
14280 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14282 this.touchViewEl.addClass('in');
14285 this.doTouchViewQuery();
14289 hideTouchView : function()
14291 this.touchViewEl.removeClass('in');
14295 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14297 this.touchViewEl.setStyle('display', 'none');
14302 setTouchViewValue : function()
14309 Roo.each(this.tickItems, function(o){
14314 this.hideTouchView();
14317 doTouchViewQuery : function()
14326 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14330 if(!this.alwaysQuery || this.mode == 'local'){
14331 this.onTouchViewLoad();
14338 onTouchViewBeforeLoad : function(combo,opts)
14344 onTouchViewLoad : function()
14346 if(this.store.getCount() < 1){
14347 this.onTouchViewEmptyResults();
14351 this.clearTouchView();
14353 var rawValue = this.getRawValue();
14355 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14357 this.tickItems = [];
14359 this.store.data.each(function(d, rowIndex){
14360 var row = this.touchViewListGroup.createChild(template);
14362 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14363 row.addClass(d.data.cls);
14366 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14369 html : d.data[this.displayField]
14372 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14373 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14376 row.removeClass('selected');
14377 if(!this.multiple && this.valueField &&
14378 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14381 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14382 row.addClass('selected');
14385 if(this.multiple && this.valueField &&
14386 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14390 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14391 this.tickItems.push(d.data);
14394 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14398 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14400 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14402 if(this.modalTitle.length){
14403 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14406 var listHeight = this.touchViewListGroup.getHeight();
14410 if(firstChecked && listHeight > bodyHeight){
14411 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14416 onTouchViewLoadException : function()
14418 this.hideTouchView();
14421 onTouchViewEmptyResults : function()
14423 this.clearTouchView();
14425 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14427 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14431 clearTouchView : function()
14433 this.touchViewListGroup.dom.innerHTML = '';
14436 onTouchViewClick : function(e, el, o)
14438 e.preventDefault();
14441 var rowIndex = o.rowIndex;
14443 var r = this.store.getAt(rowIndex);
14445 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14447 if(!this.multiple){
14448 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14449 c.dom.removeAttribute('checked');
14452 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14454 this.setFromData(r.data);
14456 var close = this.closeTriggerEl();
14462 this.hideTouchView();
14464 this.fireEvent('select', this, r, rowIndex);
14469 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14470 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14471 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14475 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14476 this.addItem(r.data);
14477 this.tickItems.push(r.data);
14483 * @cfg {Boolean} grow
14487 * @cfg {Number} growMin
14491 * @cfg {Number} growMax
14500 Roo.apply(Roo.bootstrap.ComboBox, {
14504 cls: 'modal-header',
14526 cls: 'list-group-item',
14530 cls: 'roo-combobox-list-group-item-value'
14534 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14548 listItemCheckbox : {
14550 cls: 'list-group-item',
14554 cls: 'roo-combobox-list-group-item-value'
14558 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14574 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14579 cls: 'modal-footer',
14587 cls: 'col-xs-6 text-left',
14590 cls: 'btn btn-danger roo-touch-view-cancel',
14596 cls: 'col-xs-6 text-right',
14599 cls: 'btn btn-success roo-touch-view-ok',
14610 Roo.apply(Roo.bootstrap.ComboBox, {
14612 touchViewTemplate : {
14614 cls: 'modal fade roo-combobox-touch-view',
14618 cls: 'modal-dialog',
14619 style : 'position:fixed', // we have to fix position....
14623 cls: 'modal-content',
14625 Roo.bootstrap.ComboBox.header,
14626 Roo.bootstrap.ComboBox.body,
14627 Roo.bootstrap.ComboBox.footer
14636 * Ext JS Library 1.1.1
14637 * Copyright(c) 2006-2007, Ext JS, LLC.
14639 * Originally Released Under LGPL - original licence link has changed is not relivant.
14642 * <script type="text/javascript">
14647 * @extends Roo.util.Observable
14648 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14649 * This class also supports single and multi selection modes. <br>
14650 * Create a data model bound view:
14652 var store = new Roo.data.Store(...);
14654 var view = new Roo.View({
14656 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14658 singleSelect: true,
14659 selectedClass: "ydataview-selected",
14663 // listen for node click?
14664 view.on("click", function(vw, index, node, e){
14665 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14669 dataModel.load("foobar.xml");
14671 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14673 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14674 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14676 * Note: old style constructor is still suported (container, template, config)
14679 * Create a new View
14680 * @param {Object} config The config object
14683 Roo.View = function(config, depreciated_tpl, depreciated_config){
14685 this.parent = false;
14687 if (typeof(depreciated_tpl) == 'undefined') {
14688 // new way.. - universal constructor.
14689 Roo.apply(this, config);
14690 this.el = Roo.get(this.el);
14693 this.el = Roo.get(config);
14694 this.tpl = depreciated_tpl;
14695 Roo.apply(this, depreciated_config);
14697 this.wrapEl = this.el.wrap().wrap();
14698 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14701 if(typeof(this.tpl) == "string"){
14702 this.tpl = new Roo.Template(this.tpl);
14704 // support xtype ctors..
14705 this.tpl = new Roo.factory(this.tpl, Roo);
14709 this.tpl.compile();
14714 * @event beforeclick
14715 * Fires before a click is processed. Returns false to cancel the default action.
14716 * @param {Roo.View} this
14717 * @param {Number} index The index of the target node
14718 * @param {HTMLElement} node The target node
14719 * @param {Roo.EventObject} e The raw event object
14721 "beforeclick" : true,
14724 * Fires when a template node is clicked.
14725 * @param {Roo.View} this
14726 * @param {Number} index The index of the target node
14727 * @param {HTMLElement} node The target node
14728 * @param {Roo.EventObject} e The raw event object
14733 * Fires when a template node is double clicked.
14734 * @param {Roo.View} this
14735 * @param {Number} index The index of the target node
14736 * @param {HTMLElement} node The target node
14737 * @param {Roo.EventObject} e The raw event object
14741 * @event contextmenu
14742 * Fires when a template node is right clicked.
14743 * @param {Roo.View} this
14744 * @param {Number} index The index of the target node
14745 * @param {HTMLElement} node The target node
14746 * @param {Roo.EventObject} e The raw event object
14748 "contextmenu" : true,
14750 * @event selectionchange
14751 * Fires when the selected nodes change.
14752 * @param {Roo.View} this
14753 * @param {Array} selections Array of the selected nodes
14755 "selectionchange" : true,
14758 * @event beforeselect
14759 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14760 * @param {Roo.View} this
14761 * @param {HTMLElement} node The node to be selected
14762 * @param {Array} selections Array of currently selected nodes
14764 "beforeselect" : true,
14766 * @event preparedata
14767 * Fires on every row to render, to allow you to change the data.
14768 * @param {Roo.View} this
14769 * @param {Object} data to be rendered (change this)
14771 "preparedata" : true
14779 "click": this.onClick,
14780 "dblclick": this.onDblClick,
14781 "contextmenu": this.onContextMenu,
14785 this.selections = [];
14787 this.cmp = new Roo.CompositeElementLite([]);
14789 this.store = Roo.factory(this.store, Roo.data);
14790 this.setStore(this.store, true);
14793 if ( this.footer && this.footer.xtype) {
14795 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14797 this.footer.dataSource = this.store;
14798 this.footer.container = fctr;
14799 this.footer = Roo.factory(this.footer, Roo);
14800 fctr.insertFirst(this.el);
14802 // this is a bit insane - as the paging toolbar seems to detach the el..
14803 // dom.parentNode.parentNode.parentNode
14804 // they get detached?
14808 Roo.View.superclass.constructor.call(this);
14813 Roo.extend(Roo.View, Roo.util.Observable, {
14816 * @cfg {Roo.data.Store} store Data store to load data from.
14821 * @cfg {String|Roo.Element} el The container element.
14826 * @cfg {String|Roo.Template} tpl The template used by this View
14830 * @cfg {String} dataName the named area of the template to use as the data area
14831 * Works with domtemplates roo-name="name"
14835 * @cfg {String} selectedClass The css class to add to selected nodes
14837 selectedClass : "x-view-selected",
14839 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14844 * @cfg {String} text to display on mask (default Loading)
14848 * @cfg {Boolean} multiSelect Allow multiple selection
14850 multiSelect : false,
14852 * @cfg {Boolean} singleSelect Allow single selection
14854 singleSelect: false,
14857 * @cfg {Boolean} toggleSelect - selecting
14859 toggleSelect : false,
14862 * @cfg {Boolean} tickable - selecting
14867 * Returns the element this view is bound to.
14868 * @return {Roo.Element}
14870 getEl : function(){
14871 return this.wrapEl;
14877 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14879 refresh : function(){
14880 //Roo.log('refresh');
14883 // if we are using something like 'domtemplate', then
14884 // the what gets used is:
14885 // t.applySubtemplate(NAME, data, wrapping data..)
14886 // the outer template then get' applied with
14887 // the store 'extra data'
14888 // and the body get's added to the
14889 // roo-name="data" node?
14890 // <span class='roo-tpl-{name}'></span> ?????
14894 this.clearSelections();
14895 this.el.update("");
14897 var records = this.store.getRange();
14898 if(records.length < 1) {
14900 // is this valid?? = should it render a template??
14902 this.el.update(this.emptyText);
14906 if (this.dataName) {
14907 this.el.update(t.apply(this.store.meta)); //????
14908 el = this.el.child('.roo-tpl-' + this.dataName);
14911 for(var i = 0, len = records.length; i < len; i++){
14912 var data = this.prepareData(records[i].data, i, records[i]);
14913 this.fireEvent("preparedata", this, data, i, records[i]);
14915 var d = Roo.apply({}, data);
14918 Roo.apply(d, {'roo-id' : Roo.id()});
14922 Roo.each(this.parent.item, function(item){
14923 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14926 Roo.apply(d, {'roo-data-checked' : 'checked'});
14930 html[html.length] = Roo.util.Format.trim(
14932 t.applySubtemplate(this.dataName, d, this.store.meta) :
14939 el.update(html.join(""));
14940 this.nodes = el.dom.childNodes;
14941 this.updateIndexes(0);
14946 * Function to override to reformat the data that is sent to
14947 * the template for each node.
14948 * DEPRICATED - use the preparedata event handler.
14949 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14950 * a JSON object for an UpdateManager bound view).
14952 prepareData : function(data, index, record)
14954 this.fireEvent("preparedata", this, data, index, record);
14958 onUpdate : function(ds, record){
14959 // Roo.log('on update');
14960 this.clearSelections();
14961 var index = this.store.indexOf(record);
14962 var n = this.nodes[index];
14963 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14964 n.parentNode.removeChild(n);
14965 this.updateIndexes(index, index);
14971 onAdd : function(ds, records, index)
14973 //Roo.log(['on Add', ds, records, index] );
14974 this.clearSelections();
14975 if(this.nodes.length == 0){
14979 var n = this.nodes[index];
14980 for(var i = 0, len = records.length; i < len; i++){
14981 var d = this.prepareData(records[i].data, i, records[i]);
14983 this.tpl.insertBefore(n, d);
14986 this.tpl.append(this.el, d);
14989 this.updateIndexes(index);
14992 onRemove : function(ds, record, index){
14993 // Roo.log('onRemove');
14994 this.clearSelections();
14995 var el = this.dataName ?
14996 this.el.child('.roo-tpl-' + this.dataName) :
14999 el.dom.removeChild(this.nodes[index]);
15000 this.updateIndexes(index);
15004 * Refresh an individual node.
15005 * @param {Number} index
15007 refreshNode : function(index){
15008 this.onUpdate(this.store, this.store.getAt(index));
15011 updateIndexes : function(startIndex, endIndex){
15012 var ns = this.nodes;
15013 startIndex = startIndex || 0;
15014 endIndex = endIndex || ns.length - 1;
15015 for(var i = startIndex; i <= endIndex; i++){
15016 ns[i].nodeIndex = i;
15021 * Changes the data store this view uses and refresh the view.
15022 * @param {Store} store
15024 setStore : function(store, initial){
15025 if(!initial && this.store){
15026 this.store.un("datachanged", this.refresh);
15027 this.store.un("add", this.onAdd);
15028 this.store.un("remove", this.onRemove);
15029 this.store.un("update", this.onUpdate);
15030 this.store.un("clear", this.refresh);
15031 this.store.un("beforeload", this.onBeforeLoad);
15032 this.store.un("load", this.onLoad);
15033 this.store.un("loadexception", this.onLoad);
15037 store.on("datachanged", this.refresh, this);
15038 store.on("add", this.onAdd, this);
15039 store.on("remove", this.onRemove, this);
15040 store.on("update", this.onUpdate, this);
15041 store.on("clear", this.refresh, this);
15042 store.on("beforeload", this.onBeforeLoad, this);
15043 store.on("load", this.onLoad, this);
15044 store.on("loadexception", this.onLoad, this);
15052 * onbeforeLoad - masks the loading area.
15055 onBeforeLoad : function(store,opts)
15057 //Roo.log('onBeforeLoad');
15059 this.el.update("");
15061 this.el.mask(this.mask ? this.mask : "Loading" );
15063 onLoad : function ()
15070 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15071 * @param {HTMLElement} node
15072 * @return {HTMLElement} The template node
15074 findItemFromChild : function(node){
15075 var el = this.dataName ?
15076 this.el.child('.roo-tpl-' + this.dataName,true) :
15079 if(!node || node.parentNode == el){
15082 var p = node.parentNode;
15083 while(p && p != el){
15084 if(p.parentNode == el){
15093 onClick : function(e){
15094 var item = this.findItemFromChild(e.getTarget());
15096 var index = this.indexOf(item);
15097 if(this.onItemClick(item, index, e) !== false){
15098 this.fireEvent("click", this, index, item, e);
15101 this.clearSelections();
15106 onContextMenu : function(e){
15107 var item = this.findItemFromChild(e.getTarget());
15109 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15114 onDblClick : function(e){
15115 var item = this.findItemFromChild(e.getTarget());
15117 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15121 onItemClick : function(item, index, e)
15123 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15126 if (this.toggleSelect) {
15127 var m = this.isSelected(item) ? 'unselect' : 'select';
15130 _t[m](item, true, false);
15133 if(this.multiSelect || this.singleSelect){
15134 if(this.multiSelect && e.shiftKey && this.lastSelection){
15135 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15137 this.select(item, this.multiSelect && e.ctrlKey);
15138 this.lastSelection = item;
15141 if(!this.tickable){
15142 e.preventDefault();
15150 * Get the number of selected nodes.
15153 getSelectionCount : function(){
15154 return this.selections.length;
15158 * Get the currently selected nodes.
15159 * @return {Array} An array of HTMLElements
15161 getSelectedNodes : function(){
15162 return this.selections;
15166 * Get the indexes of the selected nodes.
15169 getSelectedIndexes : function(){
15170 var indexes = [], s = this.selections;
15171 for(var i = 0, len = s.length; i < len; i++){
15172 indexes.push(s[i].nodeIndex);
15178 * Clear all selections
15179 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15181 clearSelections : function(suppressEvent){
15182 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15183 this.cmp.elements = this.selections;
15184 this.cmp.removeClass(this.selectedClass);
15185 this.selections = [];
15186 if(!suppressEvent){
15187 this.fireEvent("selectionchange", this, this.selections);
15193 * Returns true if the passed node is selected
15194 * @param {HTMLElement/Number} node The node or node index
15195 * @return {Boolean}
15197 isSelected : function(node){
15198 var s = this.selections;
15202 node = this.getNode(node);
15203 return s.indexOf(node) !== -1;
15208 * @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
15209 * @param {Boolean} keepExisting (optional) true to keep existing selections
15210 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15212 select : function(nodeInfo, keepExisting, suppressEvent){
15213 if(nodeInfo instanceof Array){
15215 this.clearSelections(true);
15217 for(var i = 0, len = nodeInfo.length; i < len; i++){
15218 this.select(nodeInfo[i], true, true);
15222 var node = this.getNode(nodeInfo);
15223 if(!node || this.isSelected(node)){
15224 return; // already selected.
15227 this.clearSelections(true);
15230 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15231 Roo.fly(node).addClass(this.selectedClass);
15232 this.selections.push(node);
15233 if(!suppressEvent){
15234 this.fireEvent("selectionchange", this, this.selections);
15242 * @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
15243 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15244 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15246 unselect : function(nodeInfo, keepExisting, suppressEvent)
15248 if(nodeInfo instanceof Array){
15249 Roo.each(this.selections, function(s) {
15250 this.unselect(s, nodeInfo);
15254 var node = this.getNode(nodeInfo);
15255 if(!node || !this.isSelected(node)){
15256 //Roo.log("not selected");
15257 return; // not selected.
15261 Roo.each(this.selections, function(s) {
15263 Roo.fly(node).removeClass(this.selectedClass);
15270 this.selections= ns;
15271 this.fireEvent("selectionchange", this, this.selections);
15275 * Gets a template node.
15276 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15277 * @return {HTMLElement} The node or null if it wasn't found
15279 getNode : function(nodeInfo){
15280 if(typeof nodeInfo == "string"){
15281 return document.getElementById(nodeInfo);
15282 }else if(typeof nodeInfo == "number"){
15283 return this.nodes[nodeInfo];
15289 * Gets a range template nodes.
15290 * @param {Number} startIndex
15291 * @param {Number} endIndex
15292 * @return {Array} An array of nodes
15294 getNodes : function(start, end){
15295 var ns = this.nodes;
15296 start = start || 0;
15297 end = typeof end == "undefined" ? ns.length - 1 : end;
15300 for(var i = start; i <= end; i++){
15304 for(var i = start; i >= end; i--){
15312 * Finds the index of the passed node
15313 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15314 * @return {Number} The index of the node or -1
15316 indexOf : function(node){
15317 node = this.getNode(node);
15318 if(typeof node.nodeIndex == "number"){
15319 return node.nodeIndex;
15321 var ns = this.nodes;
15322 for(var i = 0, len = ns.length; i < len; i++){
15333 * based on jquery fullcalendar
15337 Roo.bootstrap = Roo.bootstrap || {};
15339 * @class Roo.bootstrap.Calendar
15340 * @extends Roo.bootstrap.Component
15341 * Bootstrap Calendar class
15342 * @cfg {Boolean} loadMask (true|false) default false
15343 * @cfg {Object} header generate the user specific header of the calendar, default false
15346 * Create a new Container
15347 * @param {Object} config The config object
15352 Roo.bootstrap.Calendar = function(config){
15353 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15357 * Fires when a date is selected
15358 * @param {DatePicker} this
15359 * @param {Date} date The selected date
15363 * @event monthchange
15364 * Fires when the displayed month changes
15365 * @param {DatePicker} this
15366 * @param {Date} date The selected month
15368 'monthchange': true,
15370 * @event evententer
15371 * Fires when mouse over an event
15372 * @param {Calendar} this
15373 * @param {event} Event
15375 'evententer': true,
15377 * @event eventleave
15378 * Fires when the mouse leaves an
15379 * @param {Calendar} this
15382 'eventleave': true,
15384 * @event eventclick
15385 * Fires when the mouse click an
15386 * @param {Calendar} this
15395 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15398 * @cfg {Number} startDay
15399 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15407 getAutoCreate : function(){
15410 var fc_button = function(name, corner, style, content ) {
15411 return Roo.apply({},{
15413 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15415 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15418 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15429 style : 'width:100%',
15436 cls : 'fc-header-left',
15438 fc_button('prev', 'left', 'arrow', '‹' ),
15439 fc_button('next', 'right', 'arrow', '›' ),
15440 { tag: 'span', cls: 'fc-header-space' },
15441 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15449 cls : 'fc-header-center',
15453 cls: 'fc-header-title',
15456 html : 'month / year'
15464 cls : 'fc-header-right',
15466 /* fc_button('month', 'left', '', 'month' ),
15467 fc_button('week', '', '', 'week' ),
15468 fc_button('day', 'right', '', 'day' )
15480 header = this.header;
15483 var cal_heads = function() {
15485 // fixme - handle this.
15487 for (var i =0; i < Date.dayNames.length; i++) {
15488 var d = Date.dayNames[i];
15491 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15492 html : d.substring(0,3)
15496 ret[0].cls += ' fc-first';
15497 ret[6].cls += ' fc-last';
15500 var cal_cell = function(n) {
15503 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15508 cls: 'fc-day-number',
15512 cls: 'fc-day-content',
15516 style: 'position: relative;' // height: 17px;
15528 var cal_rows = function() {
15531 for (var r = 0; r < 6; r++) {
15538 for (var i =0; i < Date.dayNames.length; i++) {
15539 var d = Date.dayNames[i];
15540 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15543 row.cn[0].cls+=' fc-first';
15544 row.cn[0].cn[0].style = 'min-height:90px';
15545 row.cn[6].cls+=' fc-last';
15549 ret[0].cls += ' fc-first';
15550 ret[4].cls += ' fc-prev-last';
15551 ret[5].cls += ' fc-last';
15558 cls: 'fc-border-separate',
15559 style : 'width:100%',
15567 cls : 'fc-first fc-last',
15585 cls : 'fc-content',
15586 style : "position: relative;",
15589 cls : 'fc-view fc-view-month fc-grid',
15590 style : 'position: relative',
15591 unselectable : 'on',
15594 cls : 'fc-event-container',
15595 style : 'position:absolute;z-index:8;top:0;left:0;'
15613 initEvents : function()
15616 throw "can not find store for calendar";
15622 style: "text-align:center",
15626 style: "background-color:white;width:50%;margin:250 auto",
15630 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15641 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15643 var size = this.el.select('.fc-content', true).first().getSize();
15644 this.maskEl.setSize(size.width, size.height);
15645 this.maskEl.enableDisplayMode("block");
15646 if(!this.loadMask){
15647 this.maskEl.hide();
15650 this.store = Roo.factory(this.store, Roo.data);
15651 this.store.on('load', this.onLoad, this);
15652 this.store.on('beforeload', this.onBeforeLoad, this);
15656 this.cells = this.el.select('.fc-day',true);
15657 //Roo.log(this.cells);
15658 this.textNodes = this.el.query('.fc-day-number');
15659 this.cells.addClassOnOver('fc-state-hover');
15661 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15662 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15663 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15664 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15666 this.on('monthchange', this.onMonthChange, this);
15668 this.update(new Date().clearTime());
15671 resize : function() {
15672 var sz = this.el.getSize();
15674 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15675 this.el.select('.fc-day-content div',true).setHeight(34);
15680 showPrevMonth : function(e){
15681 this.update(this.activeDate.add("mo", -1));
15683 showToday : function(e){
15684 this.update(new Date().clearTime());
15687 showNextMonth : function(e){
15688 this.update(this.activeDate.add("mo", 1));
15692 showPrevYear : function(){
15693 this.update(this.activeDate.add("y", -1));
15697 showNextYear : function(){
15698 this.update(this.activeDate.add("y", 1));
15703 update : function(date)
15705 var vd = this.activeDate;
15706 this.activeDate = date;
15707 // if(vd && this.el){
15708 // var t = date.getTime();
15709 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15710 // Roo.log('using add remove');
15712 // this.fireEvent('monthchange', this, date);
15714 // this.cells.removeClass("fc-state-highlight");
15715 // this.cells.each(function(c){
15716 // if(c.dateValue == t){
15717 // c.addClass("fc-state-highlight");
15718 // setTimeout(function(){
15719 // try{c.dom.firstChild.focus();}catch(e){}
15729 var days = date.getDaysInMonth();
15731 var firstOfMonth = date.getFirstDateOfMonth();
15732 var startingPos = firstOfMonth.getDay()-this.startDay;
15734 if(startingPos < this.startDay){
15738 var pm = date.add(Date.MONTH, -1);
15739 var prevStart = pm.getDaysInMonth()-startingPos;
15741 this.cells = this.el.select('.fc-day',true);
15742 this.textNodes = this.el.query('.fc-day-number');
15743 this.cells.addClassOnOver('fc-state-hover');
15745 var cells = this.cells.elements;
15746 var textEls = this.textNodes;
15748 Roo.each(cells, function(cell){
15749 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15752 days += startingPos;
15754 // convert everything to numbers so it's fast
15755 var day = 86400000;
15756 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15759 //Roo.log(prevStart);
15761 var today = new Date().clearTime().getTime();
15762 var sel = date.clearTime().getTime();
15763 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15764 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15765 var ddMatch = this.disabledDatesRE;
15766 var ddText = this.disabledDatesText;
15767 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15768 var ddaysText = this.disabledDaysText;
15769 var format = this.format;
15771 var setCellClass = function(cal, cell){
15775 //Roo.log('set Cell Class');
15777 var t = d.getTime();
15781 cell.dateValue = t;
15783 cell.className += " fc-today";
15784 cell.className += " fc-state-highlight";
15785 cell.title = cal.todayText;
15788 // disable highlight in other month..
15789 //cell.className += " fc-state-highlight";
15794 cell.className = " fc-state-disabled";
15795 cell.title = cal.minText;
15799 cell.className = " fc-state-disabled";
15800 cell.title = cal.maxText;
15804 if(ddays.indexOf(d.getDay()) != -1){
15805 cell.title = ddaysText;
15806 cell.className = " fc-state-disabled";
15809 if(ddMatch && format){
15810 var fvalue = d.dateFormat(format);
15811 if(ddMatch.test(fvalue)){
15812 cell.title = ddText.replace("%0", fvalue);
15813 cell.className = " fc-state-disabled";
15817 if (!cell.initialClassName) {
15818 cell.initialClassName = cell.dom.className;
15821 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15826 for(; i < startingPos; i++) {
15827 textEls[i].innerHTML = (++prevStart);
15828 d.setDate(d.getDate()+1);
15830 cells[i].className = "fc-past fc-other-month";
15831 setCellClass(this, cells[i]);
15836 for(; i < days; i++){
15837 intDay = i - startingPos + 1;
15838 textEls[i].innerHTML = (intDay);
15839 d.setDate(d.getDate()+1);
15841 cells[i].className = ''; // "x-date-active";
15842 setCellClass(this, cells[i]);
15846 for(; i < 42; i++) {
15847 textEls[i].innerHTML = (++extraDays);
15848 d.setDate(d.getDate()+1);
15850 cells[i].className = "fc-future fc-other-month";
15851 setCellClass(this, cells[i]);
15854 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15856 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15858 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15859 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15861 if(totalRows != 6){
15862 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15863 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15866 this.fireEvent('monthchange', this, date);
15870 if(!this.internalRender){
15871 var main = this.el.dom.firstChild;
15872 var w = main.offsetWidth;
15873 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15874 Roo.fly(main).setWidth(w);
15875 this.internalRender = true;
15876 // opera does not respect the auto grow header center column
15877 // then, after it gets a width opera refuses to recalculate
15878 // without a second pass
15879 if(Roo.isOpera && !this.secondPass){
15880 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15881 this.secondPass = true;
15882 this.update.defer(10, this, [date]);
15889 findCell : function(dt) {
15890 dt = dt.clearTime().getTime();
15892 this.cells.each(function(c){
15893 //Roo.log("check " +c.dateValue + '?=' + dt);
15894 if(c.dateValue == dt){
15904 findCells : function(ev) {
15905 var s = ev.start.clone().clearTime().getTime();
15907 var e= ev.end.clone().clearTime().getTime();
15910 this.cells.each(function(c){
15911 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15913 if(c.dateValue > e){
15916 if(c.dateValue < s){
15925 // findBestRow: function(cells)
15929 // for (var i =0 ; i < cells.length;i++) {
15930 // ret = Math.max(cells[i].rows || 0,ret);
15937 addItem : function(ev)
15939 // look for vertical location slot in
15940 var cells = this.findCells(ev);
15942 // ev.row = this.findBestRow(cells);
15944 // work out the location.
15948 for(var i =0; i < cells.length; i++) {
15950 cells[i].row = cells[0].row;
15953 cells[i].row = cells[i].row + 1;
15963 if (crow.start.getY() == cells[i].getY()) {
15965 crow.end = cells[i];
15982 cells[0].events.push(ev);
15984 this.calevents.push(ev);
15987 clearEvents: function() {
15989 if(!this.calevents){
15993 Roo.each(this.cells.elements, function(c){
15999 Roo.each(this.calevents, function(e) {
16000 Roo.each(e.els, function(el) {
16001 el.un('mouseenter' ,this.onEventEnter, this);
16002 el.un('mouseleave' ,this.onEventLeave, this);
16007 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16013 renderEvents: function()
16017 this.cells.each(function(c) {
16026 if(c.row != c.events.length){
16027 r = 4 - (4 - (c.row - c.events.length));
16030 c.events = ev.slice(0, r);
16031 c.more = ev.slice(r);
16033 if(c.more.length && c.more.length == 1){
16034 c.events.push(c.more.pop());
16037 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16041 this.cells.each(function(c) {
16043 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16046 for (var e = 0; e < c.events.length; e++){
16047 var ev = c.events[e];
16048 var rows = ev.rows;
16050 for(var i = 0; i < rows.length; i++) {
16052 // how many rows should it span..
16055 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16056 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16058 unselectable : "on",
16061 cls: 'fc-event-inner',
16065 // cls: 'fc-event-time',
16066 // html : cells.length > 1 ? '' : ev.time
16070 cls: 'fc-event-title',
16071 html : String.format('{0}', ev.title)
16078 cls: 'ui-resizable-handle ui-resizable-e',
16079 html : '  '
16086 cfg.cls += ' fc-event-start';
16088 if ((i+1) == rows.length) {
16089 cfg.cls += ' fc-event-end';
16092 var ctr = _this.el.select('.fc-event-container',true).first();
16093 var cg = ctr.createChild(cfg);
16095 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16096 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16098 var r = (c.more.length) ? 1 : 0;
16099 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16100 cg.setWidth(ebox.right - sbox.x -2);
16102 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16103 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16104 cg.on('click', _this.onEventClick, _this, ev);
16115 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16116 style : 'position: absolute',
16117 unselectable : "on",
16120 cls: 'fc-event-inner',
16124 cls: 'fc-event-title',
16132 cls: 'ui-resizable-handle ui-resizable-e',
16133 html : '  '
16139 var ctr = _this.el.select('.fc-event-container',true).first();
16140 var cg = ctr.createChild(cfg);
16142 var sbox = c.select('.fc-day-content',true).first().getBox();
16143 var ebox = c.select('.fc-day-content',true).first().getBox();
16145 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16146 cg.setWidth(ebox.right - sbox.x -2);
16148 cg.on('click', _this.onMoreEventClick, _this, c.more);
16158 onEventEnter: function (e, el,event,d) {
16159 this.fireEvent('evententer', this, el, event);
16162 onEventLeave: function (e, el,event,d) {
16163 this.fireEvent('eventleave', this, el, event);
16166 onEventClick: function (e, el,event,d) {
16167 this.fireEvent('eventclick', this, el, event);
16170 onMonthChange: function () {
16174 onMoreEventClick: function(e, el, more)
16178 this.calpopover.placement = 'right';
16179 this.calpopover.setTitle('More');
16181 this.calpopover.setContent('');
16183 var ctr = this.calpopover.el.select('.popover-content', true).first();
16185 Roo.each(more, function(m){
16187 cls : 'fc-event-hori fc-event-draggable',
16190 var cg = ctr.createChild(cfg);
16192 cg.on('click', _this.onEventClick, _this, m);
16195 this.calpopover.show(el);
16200 onLoad: function ()
16202 this.calevents = [];
16205 if(this.store.getCount() > 0){
16206 this.store.data.each(function(d){
16209 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16210 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16211 time : d.data.start_time,
16212 title : d.data.title,
16213 description : d.data.description,
16214 venue : d.data.venue
16219 this.renderEvents();
16221 if(this.calevents.length && this.loadMask){
16222 this.maskEl.hide();
16226 onBeforeLoad: function()
16228 this.clearEvents();
16230 this.maskEl.show();
16244 * @class Roo.bootstrap.Popover
16245 * @extends Roo.bootstrap.Component
16246 * Bootstrap Popover class
16247 * @cfg {String} html contents of the popover (or false to use children..)
16248 * @cfg {String} title of popover (or false to hide)
16249 * @cfg {String} placement how it is placed
16250 * @cfg {String} trigger click || hover (or false to trigger manually)
16251 * @cfg {String} over what (parent or false to trigger manually.)
16252 * @cfg {Number} delay - delay before showing
16255 * Create a new Popover
16256 * @param {Object} config The config object
16259 Roo.bootstrap.Popover = function(config){
16260 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16266 * After the popover show
16268 * @param {Roo.bootstrap.Popover} this
16273 * After the popover hide
16275 * @param {Roo.bootstrap.Popover} this
16281 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16283 title: 'Fill in a title',
16286 placement : 'right',
16287 trigger : 'hover', // hover
16293 can_build_overlaid : false,
16295 getChildContainer : function()
16297 return this.el.select('.popover-content',true).first();
16300 getAutoCreate : function(){
16303 cls : 'popover roo-dynamic',
16304 style: 'display:block',
16310 cls : 'popover-inner',
16314 cls: 'popover-title',
16318 cls : 'popover-content',
16329 setTitle: function(str)
16332 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16334 setContent: function(str)
16337 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16339 // as it get's added to the bottom of the page.
16340 onRender : function(ct, position)
16342 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16344 var cfg = Roo.apply({}, this.getAutoCreate());
16348 cfg.cls += ' ' + this.cls;
16351 cfg.style = this.style;
16353 //Roo.log("adding to ");
16354 this.el = Roo.get(document.body).createChild(cfg, position);
16355 // Roo.log(this.el);
16360 initEvents : function()
16362 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16363 this.el.enableDisplayMode('block');
16365 if (this.over === false) {
16368 if (this.triggers === false) {
16371 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16372 var triggers = this.trigger ? this.trigger.split(' ') : [];
16373 Roo.each(triggers, function(trigger) {
16375 if (trigger == 'click') {
16376 on_el.on('click', this.toggle, this);
16377 } else if (trigger != 'manual') {
16378 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16379 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16381 on_el.on(eventIn ,this.enter, this);
16382 on_el.on(eventOut, this.leave, this);
16393 toggle : function () {
16394 this.hoverState == 'in' ? this.leave() : this.enter();
16397 enter : function () {
16399 clearTimeout(this.timeout);
16401 this.hoverState = 'in';
16403 if (!this.delay || !this.delay.show) {
16408 this.timeout = setTimeout(function () {
16409 if (_t.hoverState == 'in') {
16412 }, this.delay.show)
16415 leave : function() {
16416 clearTimeout(this.timeout);
16418 this.hoverState = 'out';
16420 if (!this.delay || !this.delay.hide) {
16425 this.timeout = setTimeout(function () {
16426 if (_t.hoverState == 'out') {
16429 }, this.delay.hide)
16432 show : function (on_el)
16435 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16439 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16440 if (this.html !== false) {
16441 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16443 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16444 if (!this.title.length) {
16445 this.el.select('.popover-title',true).hide();
16448 var placement = typeof this.placement == 'function' ?
16449 this.placement.call(this, this.el, on_el) :
16452 var autoToken = /\s?auto?\s?/i;
16453 var autoPlace = autoToken.test(placement);
16455 placement = placement.replace(autoToken, '') || 'top';
16459 //this.el.setXY([0,0]);
16461 this.el.dom.style.display='block';
16462 this.el.addClass(placement);
16464 //this.el.appendTo(on_el);
16466 var p = this.getPosition();
16467 var box = this.el.getBox();
16472 var align = Roo.bootstrap.Popover.alignment[placement];
16473 this.el.alignTo(on_el, align[0],align[1]);
16474 //var arrow = this.el.select('.arrow',true).first();
16475 //arrow.set(align[2],
16477 this.el.addClass('in');
16480 if (this.el.hasClass('fade')) {
16484 this.hoverState = 'in';
16486 this.fireEvent('show', this);
16491 this.el.setXY([0,0]);
16492 this.el.removeClass('in');
16494 this.hoverState = null;
16496 this.fireEvent('hide', this);
16501 Roo.bootstrap.Popover.alignment = {
16502 'left' : ['r-l', [-10,0], 'right'],
16503 'right' : ['l-r', [10,0], 'left'],
16504 'bottom' : ['t-b', [0,10], 'top'],
16505 'top' : [ 'b-t', [0,-10], 'bottom']
16516 * @class Roo.bootstrap.Progress
16517 * @extends Roo.bootstrap.Component
16518 * Bootstrap Progress class
16519 * @cfg {Boolean} striped striped of the progress bar
16520 * @cfg {Boolean} active animated of the progress bar
16524 * Create a new Progress
16525 * @param {Object} config The config object
16528 Roo.bootstrap.Progress = function(config){
16529 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16532 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16537 getAutoCreate : function(){
16545 cfg.cls += ' progress-striped';
16549 cfg.cls += ' active';
16568 * @class Roo.bootstrap.ProgressBar
16569 * @extends Roo.bootstrap.Component
16570 * Bootstrap ProgressBar class
16571 * @cfg {Number} aria_valuenow aria-value now
16572 * @cfg {Number} aria_valuemin aria-value min
16573 * @cfg {Number} aria_valuemax aria-value max
16574 * @cfg {String} label label for the progress bar
16575 * @cfg {String} panel (success | info | warning | danger )
16576 * @cfg {String} role role of the progress bar
16577 * @cfg {String} sr_only text
16581 * Create a new ProgressBar
16582 * @param {Object} config The config object
16585 Roo.bootstrap.ProgressBar = function(config){
16586 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16589 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16593 aria_valuemax : 100,
16599 getAutoCreate : function()
16604 cls: 'progress-bar',
16605 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16617 cfg.role = this.role;
16620 if(this.aria_valuenow){
16621 cfg['aria-valuenow'] = this.aria_valuenow;
16624 if(this.aria_valuemin){
16625 cfg['aria-valuemin'] = this.aria_valuemin;
16628 if(this.aria_valuemax){
16629 cfg['aria-valuemax'] = this.aria_valuemax;
16632 if(this.label && !this.sr_only){
16633 cfg.html = this.label;
16637 cfg.cls += ' progress-bar-' + this.panel;
16643 update : function(aria_valuenow)
16645 this.aria_valuenow = aria_valuenow;
16647 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16662 * @class Roo.bootstrap.TabGroup
16663 * @extends Roo.bootstrap.Column
16664 * Bootstrap Column class
16665 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16666 * @cfg {Boolean} carousel true to make the group behave like a carousel
16667 * @cfg {Boolean} bullets show bullets for the panels
16668 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16669 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16670 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16671 * @cfg {Boolean} showarrow (true|false) show arrow default true
16674 * Create a new TabGroup
16675 * @param {Object} config The config object
16678 Roo.bootstrap.TabGroup = function(config){
16679 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16681 this.navId = Roo.id();
16684 Roo.bootstrap.TabGroup.register(this);
16688 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16691 transition : false,
16696 slideOnTouch : false,
16699 getAutoCreate : function()
16701 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16703 cfg.cls += ' tab-content';
16705 if (this.carousel) {
16706 cfg.cls += ' carousel slide';
16709 cls : 'carousel-inner',
16713 if(this.bullets && !Roo.isTouch){
16716 cls : 'carousel-bullets',
16720 if(this.bullets_cls){
16721 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16728 cfg.cn[0].cn.push(bullets);
16731 if(this.showarrow){
16732 cfg.cn[0].cn.push({
16734 class : 'carousel-arrow',
16738 class : 'carousel-prev',
16742 class : 'fa fa-chevron-left'
16748 class : 'carousel-next',
16752 class : 'fa fa-chevron-right'
16765 initEvents: function()
16767 if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16768 this.el.on("touchstart", this.onTouchStart, this);
16771 if(this.autoslide){
16774 this.slideFn = window.setInterval(function() {
16775 _this.showPanelNext();
16779 if(this.showarrow){
16780 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16781 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16787 onTouchStart : function(e, el, o)
16789 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16793 this.showPanelNext();
16796 getChildContainer : function()
16798 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16802 * register a Navigation item
16803 * @param {Roo.bootstrap.NavItem} the navitem to add
16805 register : function(item)
16807 this.tabs.push( item);
16808 item.navId = this.navId; // not really needed..
16813 getActivePanel : function()
16816 Roo.each(this.tabs, function(t) {
16826 getPanelByName : function(n)
16829 Roo.each(this.tabs, function(t) {
16830 if (t.tabId == n) {
16838 indexOfPanel : function(p)
16841 Roo.each(this.tabs, function(t,i) {
16842 if (t.tabId == p.tabId) {
16851 * show a specific panel
16852 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16853 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16855 showPanel : function (pan)
16857 if(this.transition || typeof(pan) == 'undefined'){
16858 Roo.log("waiting for the transitionend");
16862 if (typeof(pan) == 'number') {
16863 pan = this.tabs[pan];
16866 if (typeof(pan) == 'string') {
16867 pan = this.getPanelByName(pan);
16870 var cur = this.getActivePanel();
16873 Roo.log('pan or acitve pan is undefined');
16877 if (pan.tabId == this.getActivePanel().tabId) {
16881 if (false === cur.fireEvent('beforedeactivate')) {
16885 if(this.bullets > 0 && !Roo.isTouch){
16886 this.setActiveBullet(this.indexOfPanel(pan));
16889 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16891 this.transition = true;
16892 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16893 var lr = dir == 'next' ? 'left' : 'right';
16894 pan.el.addClass(dir); // or prev
16895 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16896 cur.el.addClass(lr); // or right
16897 pan.el.addClass(lr);
16900 cur.el.on('transitionend', function() {
16901 Roo.log("trans end?");
16903 pan.el.removeClass([lr,dir]);
16904 pan.setActive(true);
16906 cur.el.removeClass([lr]);
16907 cur.setActive(false);
16909 _this.transition = false;
16911 }, this, { single: true } );
16916 cur.setActive(false);
16917 pan.setActive(true);
16922 showPanelNext : function()
16924 var i = this.indexOfPanel(this.getActivePanel());
16926 if (i >= this.tabs.length - 1 && !this.autoslide) {
16930 if (i >= this.tabs.length - 1 && this.autoslide) {
16934 this.showPanel(this.tabs[i+1]);
16937 showPanelPrev : function()
16939 var i = this.indexOfPanel(this.getActivePanel());
16941 if (i < 1 && !this.autoslide) {
16945 if (i < 1 && this.autoslide) {
16946 i = this.tabs.length;
16949 this.showPanel(this.tabs[i-1]);
16953 addBullet: function()
16955 if(!this.bullets || Roo.isTouch){
16958 var ctr = this.el.select('.carousel-bullets',true).first();
16959 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16960 var bullet = ctr.createChild({
16961 cls : 'bullet bullet-' + i
16962 },ctr.dom.lastChild);
16967 bullet.on('click', (function(e, el, o, ii, t){
16969 e.preventDefault();
16971 this.showPanel(ii);
16973 if(this.autoslide && this.slideFn){
16974 clearInterval(this.slideFn);
16975 this.slideFn = window.setInterval(function() {
16976 _this.showPanelNext();
16980 }).createDelegate(this, [i, bullet], true));
16985 setActiveBullet : function(i)
16991 Roo.each(this.el.select('.bullet', true).elements, function(el){
16992 el.removeClass('selected');
16995 var bullet = this.el.select('.bullet-' + i, true).first();
17001 bullet.addClass('selected');
17012 Roo.apply(Roo.bootstrap.TabGroup, {
17016 * register a Navigation Group
17017 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17019 register : function(navgrp)
17021 this.groups[navgrp.navId] = navgrp;
17025 * fetch a Navigation Group based on the navigation ID
17026 * if one does not exist , it will get created.
17027 * @param {string} the navgroup to add
17028 * @returns {Roo.bootstrap.NavGroup} the navgroup
17030 get: function(navId) {
17031 if (typeof(this.groups[navId]) == 'undefined') {
17032 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17034 return this.groups[navId] ;
17049 * @class Roo.bootstrap.TabPanel
17050 * @extends Roo.bootstrap.Component
17051 * Bootstrap TabPanel class
17052 * @cfg {Boolean} active panel active
17053 * @cfg {String} html panel content
17054 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17055 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17056 * @cfg {String} href click to link..
17060 * Create a new TabPanel
17061 * @param {Object} config The config object
17064 Roo.bootstrap.TabPanel = function(config){
17065 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17069 * Fires when the active status changes
17070 * @param {Roo.bootstrap.TabPanel} this
17071 * @param {Boolean} state the new state
17076 * @event beforedeactivate
17077 * Fires before a tab is de-activated - can be used to do validation on a form.
17078 * @param {Roo.bootstrap.TabPanel} this
17079 * @return {Boolean} false if there is an error
17082 'beforedeactivate': true
17085 this.tabId = this.tabId || Roo.id();
17089 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17097 getAutoCreate : function(){
17100 // item is needed for carousel - not sure if it has any effect otherwise
17101 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17102 html: this.html || ''
17106 cfg.cls += ' active';
17110 cfg.tabId = this.tabId;
17117 initEvents: function()
17119 var p = this.parent();
17120 this.navId = this.navId || p.navId;
17122 if (typeof(this.navId) != 'undefined') {
17123 // not really needed.. but just in case.. parent should be a NavGroup.
17124 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17128 var i = tg.tabs.length - 1;
17130 if(this.active && tg.bullets > 0 && i < tg.bullets){
17131 tg.setActiveBullet(i);
17135 if(this.href.length){
17136 this.el.on('click', this.onClick, this);
17141 onRender : function(ct, position)
17143 // Roo.log("Call onRender: " + this.xtype);
17145 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17153 setActive: function(state)
17155 Roo.log("panel - set active " + this.tabId + "=" + state);
17157 this.active = state;
17159 this.el.removeClass('active');
17161 } else if (!this.el.hasClass('active')) {
17162 this.el.addClass('active');
17165 this.fireEvent('changed', this, state);
17168 onClick: function(e)
17170 e.preventDefault();
17172 window.location.href = this.href;
17189 * @class Roo.bootstrap.DateField
17190 * @extends Roo.bootstrap.Input
17191 * Bootstrap DateField class
17192 * @cfg {Number} weekStart default 0
17193 * @cfg {String} viewMode default empty, (months|years)
17194 * @cfg {String} minViewMode default empty, (months|years)
17195 * @cfg {Number} startDate default -Infinity
17196 * @cfg {Number} endDate default Infinity
17197 * @cfg {Boolean} todayHighlight default false
17198 * @cfg {Boolean} todayBtn default false
17199 * @cfg {Boolean} calendarWeeks default false
17200 * @cfg {Object} daysOfWeekDisabled default empty
17201 * @cfg {Boolean} singleMode default false (true | false)
17203 * @cfg {Boolean} keyboardNavigation default true
17204 * @cfg {String} language default en
17207 * Create a new DateField
17208 * @param {Object} config The config object
17211 Roo.bootstrap.DateField = function(config){
17212 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17216 * Fires when this field show.
17217 * @param {Roo.bootstrap.DateField} this
17218 * @param {Mixed} date The date value
17223 * Fires when this field hide.
17224 * @param {Roo.bootstrap.DateField} this
17225 * @param {Mixed} date The date value
17230 * Fires when select a date.
17231 * @param {Roo.bootstrap.DateField} this
17232 * @param {Mixed} date The date value
17236 * @event beforeselect
17237 * Fires when before select a date.
17238 * @param {Roo.bootstrap.DateField} this
17239 * @param {Mixed} date The date value
17241 beforeselect : true
17245 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17248 * @cfg {String} format
17249 * The default date format string which can be overriden for localization support. The format must be
17250 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17254 * @cfg {String} altFormats
17255 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17256 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17258 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17266 todayHighlight : false,
17272 keyboardNavigation: true,
17274 calendarWeeks: false,
17276 startDate: -Infinity,
17280 daysOfWeekDisabled: [],
17284 singleMode : false,
17286 UTCDate: function()
17288 return new Date(Date.UTC.apply(Date, arguments));
17291 UTCToday: function()
17293 var today = new Date();
17294 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17297 getDate: function() {
17298 var d = this.getUTCDate();
17299 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17302 getUTCDate: function() {
17306 setDate: function(d) {
17307 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17310 setUTCDate: function(d) {
17312 this.setValue(this.formatDate(this.date));
17315 onRender: function(ct, position)
17318 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17320 this.language = this.language || 'en';
17321 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17322 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17324 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17325 this.format = this.format || 'm/d/y';
17326 this.isInline = false;
17327 this.isInput = true;
17328 this.component = this.el.select('.add-on', true).first() || false;
17329 this.component = (this.component && this.component.length === 0) ? false : this.component;
17330 this.hasInput = this.component && this.inputEL().length;
17332 if (typeof(this.minViewMode === 'string')) {
17333 switch (this.minViewMode) {
17335 this.minViewMode = 1;
17338 this.minViewMode = 2;
17341 this.minViewMode = 0;
17346 if (typeof(this.viewMode === 'string')) {
17347 switch (this.viewMode) {
17360 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17362 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17364 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17366 this.picker().on('mousedown', this.onMousedown, this);
17367 this.picker().on('click', this.onClick, this);
17369 this.picker().addClass('datepicker-dropdown');
17371 this.startViewMode = this.viewMode;
17373 if(this.singleMode){
17374 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17375 v.setVisibilityMode(Roo.Element.DISPLAY);
17379 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17380 v.setStyle('width', '189px');
17384 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17385 if(!this.calendarWeeks){
17390 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17391 v.attr('colspan', function(i, val){
17392 return parseInt(val) + 1;
17397 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17399 this.setStartDate(this.startDate);
17400 this.setEndDate(this.endDate);
17402 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17409 if(this.isInline) {
17414 picker : function()
17416 return this.pickerEl;
17417 // return this.el.select('.datepicker', true).first();
17420 fillDow: function()
17422 var dowCnt = this.weekStart;
17431 if(this.calendarWeeks){
17439 while (dowCnt < this.weekStart + 7) {
17443 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17447 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17450 fillMonths: function()
17453 var months = this.picker().select('>.datepicker-months td', true).first();
17455 months.dom.innerHTML = '';
17461 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17464 months.createChild(month);
17471 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;
17473 if (this.date < this.startDate) {
17474 this.viewDate = new Date(this.startDate);
17475 } else if (this.date > this.endDate) {
17476 this.viewDate = new Date(this.endDate);
17478 this.viewDate = new Date(this.date);
17486 var d = new Date(this.viewDate),
17487 year = d.getUTCFullYear(),
17488 month = d.getUTCMonth(),
17489 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17490 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17491 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17492 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17493 currentDate = this.date && this.date.valueOf(),
17494 today = this.UTCToday();
17496 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17498 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17500 // this.picker.select('>tfoot th.today').
17501 // .text(dates[this.language].today)
17502 // .toggle(this.todayBtn !== false);
17504 this.updateNavArrows();
17507 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17509 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17511 prevMonth.setUTCDate(day);
17513 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17515 var nextMonth = new Date(prevMonth);
17517 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17519 nextMonth = nextMonth.valueOf();
17521 var fillMonths = false;
17523 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17525 while(prevMonth.valueOf() < nextMonth) {
17528 if (prevMonth.getUTCDay() === this.weekStart) {
17530 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17538 if(this.calendarWeeks){
17539 // ISO 8601: First week contains first thursday.
17540 // ISO also states week starts on Monday, but we can be more abstract here.
17542 // Start of current week: based on weekstart/current date
17543 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17544 // Thursday of this week
17545 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17546 // First Thursday of year, year from thursday
17547 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17548 // Calendar week: ms between thursdays, div ms per day, div 7 days
17549 calWeek = (th - yth) / 864e5 / 7 + 1;
17551 fillMonths.cn.push({
17559 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17561 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17564 if (this.todayHighlight &&
17565 prevMonth.getUTCFullYear() == today.getFullYear() &&
17566 prevMonth.getUTCMonth() == today.getMonth() &&
17567 prevMonth.getUTCDate() == today.getDate()) {
17568 clsName += ' today';
17571 if (currentDate && prevMonth.valueOf() === currentDate) {
17572 clsName += ' active';
17575 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17576 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17577 clsName += ' disabled';
17580 fillMonths.cn.push({
17582 cls: 'day ' + clsName,
17583 html: prevMonth.getDate()
17586 prevMonth.setDate(prevMonth.getDate()+1);
17589 var currentYear = this.date && this.date.getUTCFullYear();
17590 var currentMonth = this.date && this.date.getUTCMonth();
17592 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17594 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17595 v.removeClass('active');
17597 if(currentYear === year && k === currentMonth){
17598 v.addClass('active');
17601 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17602 v.addClass('disabled');
17608 year = parseInt(year/10, 10) * 10;
17610 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17612 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17615 for (var i = -1; i < 11; i++) {
17616 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17618 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17626 showMode: function(dir)
17629 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17632 Roo.each(this.picker().select('>div',true).elements, function(v){
17633 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17636 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17641 if(this.isInline) {
17645 this.picker().removeClass(['bottom', 'top']);
17647 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17649 * place to the top of element!
17653 this.picker().addClass('top');
17654 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17659 this.picker().addClass('bottom');
17661 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17664 parseDate : function(value)
17666 if(!value || value instanceof Date){
17669 var v = Date.parseDate(value, this.format);
17670 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17671 v = Date.parseDate(value, 'Y-m-d');
17673 if(!v && this.altFormats){
17674 if(!this.altFormatsArray){
17675 this.altFormatsArray = this.altFormats.split("|");
17677 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17678 v = Date.parseDate(value, this.altFormatsArray[i]);
17684 formatDate : function(date, fmt)
17686 return (!date || !(date instanceof Date)) ?
17687 date : date.dateFormat(fmt || this.format);
17690 onFocus : function()
17692 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17696 onBlur : function()
17698 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17700 var d = this.inputEl().getValue();
17709 this.picker().show();
17713 this.fireEvent('show', this, this.date);
17718 if(this.isInline) {
17721 this.picker().hide();
17722 this.viewMode = this.startViewMode;
17725 this.fireEvent('hide', this, this.date);
17729 onMousedown: function(e)
17731 e.stopPropagation();
17732 e.preventDefault();
17737 Roo.bootstrap.DateField.superclass.keyup.call(this);
17741 setValue: function(v)
17743 if(this.fireEvent('beforeselect', this, v) !== false){
17744 var d = new Date(this.parseDate(v) ).clearTime();
17746 if(isNaN(d.getTime())){
17747 this.date = this.viewDate = '';
17748 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17752 v = this.formatDate(d);
17754 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17756 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17760 this.fireEvent('select', this, this.date);
17764 getValue: function()
17766 return this.formatDate(this.date);
17769 fireKey: function(e)
17771 if (!this.picker().isVisible()){
17772 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17778 var dateChanged = false,
17780 newDate, newViewDate;
17785 e.preventDefault();
17789 if (!this.keyboardNavigation) {
17792 dir = e.keyCode == 37 ? -1 : 1;
17795 newDate = this.moveYear(this.date, dir);
17796 newViewDate = this.moveYear(this.viewDate, dir);
17797 } else if (e.shiftKey){
17798 newDate = this.moveMonth(this.date, dir);
17799 newViewDate = this.moveMonth(this.viewDate, dir);
17801 newDate = new Date(this.date);
17802 newDate.setUTCDate(this.date.getUTCDate() + dir);
17803 newViewDate = new Date(this.viewDate);
17804 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17806 if (this.dateWithinRange(newDate)){
17807 this.date = newDate;
17808 this.viewDate = newViewDate;
17809 this.setValue(this.formatDate(this.date));
17811 e.preventDefault();
17812 dateChanged = true;
17817 if (!this.keyboardNavigation) {
17820 dir = e.keyCode == 38 ? -1 : 1;
17822 newDate = this.moveYear(this.date, dir);
17823 newViewDate = this.moveYear(this.viewDate, dir);
17824 } else if (e.shiftKey){
17825 newDate = this.moveMonth(this.date, dir);
17826 newViewDate = this.moveMonth(this.viewDate, dir);
17828 newDate = new Date(this.date);
17829 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17830 newViewDate = new Date(this.viewDate);
17831 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17833 if (this.dateWithinRange(newDate)){
17834 this.date = newDate;
17835 this.viewDate = newViewDate;
17836 this.setValue(this.formatDate(this.date));
17838 e.preventDefault();
17839 dateChanged = true;
17843 this.setValue(this.formatDate(this.date));
17845 e.preventDefault();
17848 this.setValue(this.formatDate(this.date));
17862 onClick: function(e)
17864 e.stopPropagation();
17865 e.preventDefault();
17867 var target = e.getTarget();
17869 if(target.nodeName.toLowerCase() === 'i'){
17870 target = Roo.get(target).dom.parentNode;
17873 var nodeName = target.nodeName;
17874 var className = target.className;
17875 var html = target.innerHTML;
17876 //Roo.log(nodeName);
17878 switch(nodeName.toLowerCase()) {
17880 switch(className) {
17886 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17887 switch(this.viewMode){
17889 this.viewDate = this.moveMonth(this.viewDate, dir);
17893 this.viewDate = this.moveYear(this.viewDate, dir);
17899 var date = new Date();
17900 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17902 this.setValue(this.formatDate(this.date));
17909 if (className.indexOf('disabled') < 0) {
17910 this.viewDate.setUTCDate(1);
17911 if (className.indexOf('month') > -1) {
17912 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17914 var year = parseInt(html, 10) || 0;
17915 this.viewDate.setUTCFullYear(year);
17919 if(this.singleMode){
17920 this.setValue(this.formatDate(this.viewDate));
17931 //Roo.log(className);
17932 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17933 var day = parseInt(html, 10) || 1;
17934 var year = this.viewDate.getUTCFullYear(),
17935 month = this.viewDate.getUTCMonth();
17937 if (className.indexOf('old') > -1) {
17944 } else if (className.indexOf('new') > -1) {
17952 //Roo.log([year,month,day]);
17953 this.date = this.UTCDate(year, month, day,0,0,0,0);
17954 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17956 //Roo.log(this.formatDate(this.date));
17957 this.setValue(this.formatDate(this.date));
17964 setStartDate: function(startDate)
17966 this.startDate = startDate || -Infinity;
17967 if (this.startDate !== -Infinity) {
17968 this.startDate = this.parseDate(this.startDate);
17971 this.updateNavArrows();
17974 setEndDate: function(endDate)
17976 this.endDate = endDate || Infinity;
17977 if (this.endDate !== Infinity) {
17978 this.endDate = this.parseDate(this.endDate);
17981 this.updateNavArrows();
17984 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17986 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17987 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17988 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17990 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17991 return parseInt(d, 10);
17994 this.updateNavArrows();
17997 updateNavArrows: function()
17999 if(this.singleMode){
18003 var d = new Date(this.viewDate),
18004 year = d.getUTCFullYear(),
18005 month = d.getUTCMonth();
18007 Roo.each(this.picker().select('.prev', true).elements, function(v){
18009 switch (this.viewMode) {
18012 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18018 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18025 Roo.each(this.picker().select('.next', true).elements, function(v){
18027 switch (this.viewMode) {
18030 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18036 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18044 moveMonth: function(date, dir)
18049 var new_date = new Date(date.valueOf()),
18050 day = new_date.getUTCDate(),
18051 month = new_date.getUTCMonth(),
18052 mag = Math.abs(dir),
18054 dir = dir > 0 ? 1 : -1;
18057 // If going back one month, make sure month is not current month
18058 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18060 return new_date.getUTCMonth() == month;
18062 // If going forward one month, make sure month is as expected
18063 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18065 return new_date.getUTCMonth() != new_month;
18067 new_month = month + dir;
18068 new_date.setUTCMonth(new_month);
18069 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18070 if (new_month < 0 || new_month > 11) {
18071 new_month = (new_month + 12) % 12;
18074 // For magnitudes >1, move one month at a time...
18075 for (var i=0; i<mag; i++) {
18076 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18077 new_date = this.moveMonth(new_date, dir);
18079 // ...then reset the day, keeping it in the new month
18080 new_month = new_date.getUTCMonth();
18081 new_date.setUTCDate(day);
18083 return new_month != new_date.getUTCMonth();
18086 // Common date-resetting loop -- if date is beyond end of month, make it
18089 new_date.setUTCDate(--day);
18090 new_date.setUTCMonth(new_month);
18095 moveYear: function(date, dir)
18097 return this.moveMonth(date, dir*12);
18100 dateWithinRange: function(date)
18102 return date >= this.startDate && date <= this.endDate;
18108 this.picker().remove();
18113 Roo.apply(Roo.bootstrap.DateField, {
18124 html: '<i class="fa fa-arrow-left"/>'
18134 html: '<i class="fa fa-arrow-right"/>'
18176 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18177 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18178 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18179 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18180 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18193 navFnc: 'FullYear',
18198 navFnc: 'FullYear',
18203 Roo.apply(Roo.bootstrap.DateField, {
18207 cls: 'datepicker dropdown-menu roo-dynamic',
18211 cls: 'datepicker-days',
18215 cls: 'table-condensed',
18217 Roo.bootstrap.DateField.head,
18221 Roo.bootstrap.DateField.footer
18228 cls: 'datepicker-months',
18232 cls: 'table-condensed',
18234 Roo.bootstrap.DateField.head,
18235 Roo.bootstrap.DateField.content,
18236 Roo.bootstrap.DateField.footer
18243 cls: 'datepicker-years',
18247 cls: 'table-condensed',
18249 Roo.bootstrap.DateField.head,
18250 Roo.bootstrap.DateField.content,
18251 Roo.bootstrap.DateField.footer
18270 * @class Roo.bootstrap.TimeField
18271 * @extends Roo.bootstrap.Input
18272 * Bootstrap DateField class
18276 * Create a new TimeField
18277 * @param {Object} config The config object
18280 Roo.bootstrap.TimeField = function(config){
18281 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18285 * Fires when this field show.
18286 * @param {Roo.bootstrap.DateField} thisthis
18287 * @param {Mixed} date The date value
18292 * Fires when this field hide.
18293 * @param {Roo.bootstrap.DateField} this
18294 * @param {Mixed} date The date value
18299 * Fires when select a date.
18300 * @param {Roo.bootstrap.DateField} this
18301 * @param {Mixed} date The date value
18307 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18310 * @cfg {String} format
18311 * The default time format string which can be overriden for localization support. The format must be
18312 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18316 onRender: function(ct, position)
18319 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18321 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18323 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18325 this.pop = this.picker().select('>.datepicker-time',true).first();
18326 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18328 this.picker().on('mousedown', this.onMousedown, this);
18329 this.picker().on('click', this.onClick, this);
18331 this.picker().addClass('datepicker-dropdown');
18336 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18337 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18338 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18339 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18340 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18341 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18345 fireKey: function(e){
18346 if (!this.picker().isVisible()){
18347 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18353 e.preventDefault();
18361 this.onTogglePeriod();
18364 this.onIncrementMinutes();
18367 this.onDecrementMinutes();
18376 onClick: function(e) {
18377 e.stopPropagation();
18378 e.preventDefault();
18381 picker : function()
18383 return this.el.select('.datepicker', true).first();
18386 fillTime: function()
18388 var time = this.pop.select('tbody', true).first();
18390 time.dom.innerHTML = '';
18405 cls: 'hours-up glyphicon glyphicon-chevron-up'
18425 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18446 cls: 'timepicker-hour',
18461 cls: 'timepicker-minute',
18476 cls: 'btn btn-primary period',
18498 cls: 'hours-down glyphicon glyphicon-chevron-down'
18518 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18536 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18543 var hours = this.time.getHours();
18544 var minutes = this.time.getMinutes();
18557 hours = hours - 12;
18561 hours = '0' + hours;
18565 minutes = '0' + minutes;
18568 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18569 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18570 this.pop.select('button', true).first().dom.innerHTML = period;
18576 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18578 var cls = ['bottom'];
18580 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18587 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18592 this.picker().addClass(cls.join('-'));
18596 Roo.each(cls, function(c){
18598 _this.picker().setTop(_this.inputEl().getHeight());
18602 _this.picker().setTop(0 - _this.picker().getHeight());
18607 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18611 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18618 onFocus : function()
18620 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18624 onBlur : function()
18626 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18632 this.picker().show();
18637 this.fireEvent('show', this, this.date);
18642 this.picker().hide();
18645 this.fireEvent('hide', this, this.date);
18648 setTime : function()
18651 this.setValue(this.time.format(this.format));
18653 this.fireEvent('select', this, this.date);
18658 onMousedown: function(e){
18659 e.stopPropagation();
18660 e.preventDefault();
18663 onIncrementHours: function()
18665 Roo.log('onIncrementHours');
18666 this.time = this.time.add(Date.HOUR, 1);
18671 onDecrementHours: function()
18673 Roo.log('onDecrementHours');
18674 this.time = this.time.add(Date.HOUR, -1);
18678 onIncrementMinutes: function()
18680 Roo.log('onIncrementMinutes');
18681 this.time = this.time.add(Date.MINUTE, 1);
18685 onDecrementMinutes: function()
18687 Roo.log('onDecrementMinutes');
18688 this.time = this.time.add(Date.MINUTE, -1);
18692 onTogglePeriod: function()
18694 Roo.log('onTogglePeriod');
18695 this.time = this.time.add(Date.HOUR, 12);
18702 Roo.apply(Roo.bootstrap.TimeField, {
18732 cls: 'btn btn-info ok',
18744 Roo.apply(Roo.bootstrap.TimeField, {
18748 cls: 'datepicker dropdown-menu',
18752 cls: 'datepicker-time',
18756 cls: 'table-condensed',
18758 Roo.bootstrap.TimeField.content,
18759 Roo.bootstrap.TimeField.footer
18778 * @class Roo.bootstrap.MonthField
18779 * @extends Roo.bootstrap.Input
18780 * Bootstrap MonthField class
18782 * @cfg {String} language default en
18785 * Create a new MonthField
18786 * @param {Object} config The config object
18789 Roo.bootstrap.MonthField = function(config){
18790 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18795 * Fires when this field show.
18796 * @param {Roo.bootstrap.MonthField} this
18797 * @param {Mixed} date The date value
18802 * Fires when this field hide.
18803 * @param {Roo.bootstrap.MonthField} this
18804 * @param {Mixed} date The date value
18809 * Fires when select a date.
18810 * @param {Roo.bootstrap.MonthField} this
18811 * @param {String} oldvalue The old value
18812 * @param {String} newvalue The new value
18818 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18820 onRender: function(ct, position)
18823 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18825 this.language = this.language || 'en';
18826 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18827 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18829 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18830 this.isInline = false;
18831 this.isInput = true;
18832 this.component = this.el.select('.add-on', true).first() || false;
18833 this.component = (this.component && this.component.length === 0) ? false : this.component;
18834 this.hasInput = this.component && this.inputEL().length;
18836 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18838 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18840 this.picker().on('mousedown', this.onMousedown, this);
18841 this.picker().on('click', this.onClick, this);
18843 this.picker().addClass('datepicker-dropdown');
18845 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18846 v.setStyle('width', '189px');
18853 if(this.isInline) {
18859 setValue: function(v, suppressEvent)
18861 var o = this.getValue();
18863 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18867 if(suppressEvent !== true){
18868 this.fireEvent('select', this, o, v);
18873 getValue: function()
18878 onClick: function(e)
18880 e.stopPropagation();
18881 e.preventDefault();
18883 var target = e.getTarget();
18885 if(target.nodeName.toLowerCase() === 'i'){
18886 target = Roo.get(target).dom.parentNode;
18889 var nodeName = target.nodeName;
18890 var className = target.className;
18891 var html = target.innerHTML;
18893 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18897 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18899 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18905 picker : function()
18907 return this.pickerEl;
18910 fillMonths: function()
18913 var months = this.picker().select('>.datepicker-months td', true).first();
18915 months.dom.innerHTML = '';
18921 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18924 months.createChild(month);
18933 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18934 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18937 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18938 e.removeClass('active');
18940 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18941 e.addClass('active');
18948 if(this.isInline) {
18952 this.picker().removeClass(['bottom', 'top']);
18954 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18956 * place to the top of element!
18960 this.picker().addClass('top');
18961 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18966 this.picker().addClass('bottom');
18968 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18971 onFocus : function()
18973 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18977 onBlur : function()
18979 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18981 var d = this.inputEl().getValue();
18990 this.picker().show();
18991 this.picker().select('>.datepicker-months', true).first().show();
18995 this.fireEvent('show', this, this.date);
19000 if(this.isInline) {
19003 this.picker().hide();
19004 this.fireEvent('hide', this, this.date);
19008 onMousedown: function(e)
19010 e.stopPropagation();
19011 e.preventDefault();
19016 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19020 fireKey: function(e)
19022 if (!this.picker().isVisible()){
19023 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19034 e.preventDefault();
19038 dir = e.keyCode == 37 ? -1 : 1;
19040 this.vIndex = this.vIndex + dir;
19042 if(this.vIndex < 0){
19046 if(this.vIndex > 11){
19050 if(isNaN(this.vIndex)){
19054 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19060 dir = e.keyCode == 38 ? -1 : 1;
19062 this.vIndex = this.vIndex + dir * 4;
19064 if(this.vIndex < 0){
19068 if(this.vIndex > 11){
19072 if(isNaN(this.vIndex)){
19076 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19081 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19082 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19086 e.preventDefault();
19089 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19090 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19106 this.picker().remove();
19111 Roo.apply(Roo.bootstrap.MonthField, {
19130 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19131 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19136 Roo.apply(Roo.bootstrap.MonthField, {
19140 cls: 'datepicker dropdown-menu roo-dynamic',
19144 cls: 'datepicker-months',
19148 cls: 'table-condensed',
19150 Roo.bootstrap.DateField.content
19170 * @class Roo.bootstrap.CheckBox
19171 * @extends Roo.bootstrap.Input
19172 * Bootstrap CheckBox class
19174 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19175 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19176 * @cfg {String} boxLabel The text that appears beside the checkbox
19177 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19178 * @cfg {Boolean} checked initnal the element
19179 * @cfg {Boolean} inline inline the element (default false)
19180 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19183 * Create a new CheckBox
19184 * @param {Object} config The config object
19187 Roo.bootstrap.CheckBox = function(config){
19188 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19193 * Fires when the element is checked or unchecked.
19194 * @param {Roo.bootstrap.CheckBox} this This input
19195 * @param {Boolean} checked The new checked value
19202 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19204 inputType: 'checkbox',
19212 getAutoCreate : function()
19214 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19220 cfg.cls = 'form-group ' + this.inputType; //input-group
19223 cfg.cls += ' ' + this.inputType + '-inline';
19229 type : this.inputType,
19230 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19231 cls : 'roo-' + this.inputType, //'form-box',
19232 placeholder : this.placeholder || ''
19236 if (this.weight) { // Validity check?
19237 cfg.cls += " " + this.inputType + "-" + this.weight;
19240 if (this.disabled) {
19241 input.disabled=true;
19245 input.checked = this.checked;
19249 input.name = this.name;
19253 input.cls += ' input-' + this.size;
19258 ['xs','sm','md','lg'].map(function(size){
19259 if (settings[size]) {
19260 cfg.cls += ' col-' + size + '-' + settings[size];
19264 var inputblock = input;
19266 if (this.before || this.after) {
19269 cls : 'input-group',
19274 inputblock.cn.push({
19276 cls : 'input-group-addon',
19281 inputblock.cn.push(input);
19284 inputblock.cn.push({
19286 cls : 'input-group-addon',
19293 if (align ==='left' && this.fieldLabel.length) {
19294 // Roo.log("left and has label");
19300 cls : 'control-label col-md-' + this.labelWidth,
19301 html : this.fieldLabel
19305 cls : "col-md-" + (12 - this.labelWidth),
19312 } else if ( this.fieldLabel.length) {
19313 // Roo.log(" label");
19317 tag: this.boxLabel ? 'span' : 'label',
19319 cls: 'control-label box-input-label',
19320 //cls : 'input-group-addon',
19321 html : this.fieldLabel
19331 // Roo.log(" no label && no align");
19332 cfg.cn = [ inputblock ] ;
19338 var boxLabelCfg = {
19340 //'for': id, // box label is handled by onclick - so no for...
19342 html: this.boxLabel
19346 boxLabelCfg.tooltip = this.tooltip;
19349 cfg.cn.push(boxLabelCfg);
19359 * return the real input element.
19361 inputEl: function ()
19363 return this.el.select('input.roo-' + this.inputType,true).first();
19366 labelEl: function()
19368 return this.el.select('label.control-label',true).first();
19370 /* depricated... */
19374 return this.labelEl();
19377 boxLabelEl: function()
19379 return this.el.select('label.box-label',true).first();
19382 initEvents : function()
19384 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19386 this.inputEl().on('click', this.onClick, this);
19388 if (this.boxLabel) {
19389 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19392 this.startValue = this.getValue();
19395 Roo.bootstrap.CheckBox.register(this);
19399 onClick : function()
19401 this.setChecked(!this.checked);
19404 setChecked : function(state,suppressEvent)
19406 this.startValue = this.getValue();
19408 if(this.inputType == 'radio'){
19410 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19411 e.dom.checked = false;
19414 this.inputEl().dom.checked = true;
19416 this.inputEl().dom.value = this.inputValue;
19418 if(suppressEvent !== true){
19419 this.fireEvent('check', this, true);
19427 this.checked = state;
19429 this.inputEl().dom.checked = state;
19431 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19433 if(suppressEvent !== true){
19434 this.fireEvent('check', this, state);
19440 getValue : function()
19442 if(this.inputType == 'radio'){
19443 return this.getGroupValue();
19446 return this.inputEl().getValue();
19450 getGroupValue : function()
19452 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19456 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19459 setValue : function(v,suppressEvent)
19461 if(this.inputType == 'radio'){
19462 this.setGroupValue(v, suppressEvent);
19466 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19471 setGroupValue : function(v, suppressEvent)
19473 this.startValue = this.getValue();
19475 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19476 e.dom.checked = false;
19478 if(e.dom.value == v){
19479 e.dom.checked = true;
19483 if(suppressEvent !== true){
19484 this.fireEvent('check', this, true);
19492 validate : function()
19496 (this.inputType == 'radio' && this.validateRadio()) ||
19497 (this.inputType == 'checkbox' && this.validateCheckbox())
19503 this.markInvalid();
19507 validateRadio : function()
19511 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19512 if(!e.dom.checked){
19524 validateCheckbox : function()
19527 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19530 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19538 for(var i in group){
19543 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19550 * Mark this field as valid
19552 markValid : function()
19554 if(this.allowBlank){
19560 this.fireEvent('valid', this);
19562 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19565 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19572 if(this.inputType == 'radio'){
19573 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19574 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19575 e.findParent('.form-group', false, true).addClass(_this.validClass);
19582 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19583 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19587 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19593 for(var i in group){
19594 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19595 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19600 * Mark this field as invalid
19601 * @param {String} msg The validation message
19603 markInvalid : function(msg)
19605 if(this.allowBlank){
19611 this.fireEvent('invalid', this, msg);
19613 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19616 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19620 label.markInvalid();
19623 if(this.inputType == 'radio'){
19624 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19625 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19626 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19633 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19634 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19638 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19644 for(var i in group){
19645 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19646 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19653 Roo.apply(Roo.bootstrap.CheckBox, {
19658 * register a CheckBox Group
19659 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19661 register : function(checkbox)
19663 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19664 this.groups[checkbox.groupId] = {};
19667 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19671 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19675 * fetch a CheckBox Group based on the group ID
19676 * @param {string} the group ID
19677 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19679 get: function(groupId) {
19680 if (typeof(this.groups[groupId]) == 'undefined') {
19684 return this.groups[groupId] ;
19696 *<div class="radio">
19698 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19699 Option one is this and that—be sure to include why it's great
19706 *<label class="radio-inline">fieldLabel</label>
19707 *<label class="radio-inline">
19708 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19716 * @class Roo.bootstrap.Radio
19717 * @extends Roo.bootstrap.CheckBox
19718 * Bootstrap Radio class
19721 * Create a new Radio
19722 * @param {Object} config The config object
19725 Roo.bootstrap.Radio = function(config){
19726 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19730 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19732 inputType: 'radio',
19736 getAutoCreate : function()
19738 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19739 align = align || 'left'; // default...
19746 tag : this.inline ? 'span' : 'div',
19751 var inline = this.inline ? ' radio-inline' : '';
19755 // does not need for, as we wrap the input with it..
19757 cls : 'control-label box-label' + inline,
19760 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19764 //cls : 'control-label' + inline,
19765 html : this.fieldLabel,
19766 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19775 type : this.inputType,
19776 //value : (!this.checked) ? this.valueOff : this.inputValue,
19777 value : this.inputValue,
19779 placeholder : this.placeholder || '' // ?? needed????
19782 if (this.weight) { // Validity check?
19783 input.cls += " radio-" + this.weight;
19785 if (this.disabled) {
19786 input.disabled=true;
19790 input.checked = this.checked;
19794 input.name = this.name;
19798 input.cls += ' input-' + this.size;
19801 //?? can span's inline have a width??
19804 ['xs','sm','md','lg'].map(function(size){
19805 if (settings[size]) {
19806 cfg.cls += ' col-' + size + '-' + settings[size];
19810 var inputblock = input;
19812 if (this.before || this.after) {
19815 cls : 'input-group',
19820 inputblock.cn.push({
19822 cls : 'input-group-addon',
19826 inputblock.cn.push(input);
19828 inputblock.cn.push({
19830 cls : 'input-group-addon',
19838 if (this.fieldLabel && this.fieldLabel.length) {
19839 cfg.cn.push(fieldLabel);
19842 // normal bootstrap puts the input inside the label.
19843 // however with our styled version - it has to go after the input.
19845 //lbl.cn.push(inputblock);
19849 cls: 'radio' + inline,
19856 cfg.cn.push( lblwrap);
19861 html: this.boxLabel
19870 initEvents : function()
19872 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19874 this.inputEl().on('click', this.onClick, this);
19875 if (this.boxLabel) {
19876 //Roo.log('find label');
19877 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19882 inputEl: function ()
19884 return this.el.select('input.roo-radio',true).first();
19886 onClick : function()
19889 this.setChecked(true);
19892 setChecked : function(state,suppressEvent)
19895 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19896 v.dom.checked = false;
19899 Roo.log(this.inputEl().dom);
19900 this.checked = state;
19901 this.inputEl().dom.checked = state;
19903 if(suppressEvent !== true){
19904 this.fireEvent('check', this, state);
19907 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19911 getGroupValue : function()
19914 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19915 if(v.dom.checked == true){
19916 value = v.dom.value;
19924 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19925 * @return {Mixed} value The field value
19927 getValue : function(){
19928 return this.getGroupValue();
19934 //<script type="text/javascript">
19937 * Based Ext JS Library 1.1.1
19938 * Copyright(c) 2006-2007, Ext JS, LLC.
19944 * @class Roo.HtmlEditorCore
19945 * @extends Roo.Component
19946 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19948 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19951 Roo.HtmlEditorCore = function(config){
19954 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19959 * @event initialize
19960 * Fires when the editor is fully initialized (including the iframe)
19961 * @param {Roo.HtmlEditorCore} this
19966 * Fires when the editor is first receives the focus. Any insertion must wait
19967 * until after this event.
19968 * @param {Roo.HtmlEditorCore} this
19972 * @event beforesync
19973 * Fires before the textarea is updated with content from the editor iframe. Return false
19974 * to cancel the sync.
19975 * @param {Roo.HtmlEditorCore} this
19976 * @param {String} html
19980 * @event beforepush
19981 * Fires before the iframe editor is updated with content from the textarea. Return false
19982 * to cancel the push.
19983 * @param {Roo.HtmlEditorCore} this
19984 * @param {String} html
19989 * Fires when the textarea is updated with content from the editor iframe.
19990 * @param {Roo.HtmlEditorCore} this
19991 * @param {String} html
19996 * Fires when the iframe editor is updated with content from the textarea.
19997 * @param {Roo.HtmlEditorCore} this
19998 * @param {String} html
20003 * @event editorevent
20004 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20005 * @param {Roo.HtmlEditorCore} this
20011 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20013 // defaults : white / black...
20014 this.applyBlacklists();
20021 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20025 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20031 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20036 * @cfg {Number} height (in pixels)
20040 * @cfg {Number} width (in pixels)
20045 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20048 stylesheets: false,
20053 // private properties
20054 validationEvent : false,
20056 initialized : false,
20058 sourceEditMode : false,
20059 onFocus : Roo.emptyFn,
20061 hideMode:'offsets',
20065 // blacklist + whitelisted elements..
20072 * Protected method that will not generally be called directly. It
20073 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20074 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20076 getDocMarkup : function(){
20080 // inherit styels from page...??
20081 if (this.stylesheets === false) {
20083 Roo.get(document.head).select('style').each(function(node) {
20084 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20087 Roo.get(document.head).select('link').each(function(node) {
20088 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20091 } else if (!this.stylesheets.length) {
20093 st = '<style type="text/css">' +
20094 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20100 st += '<style type="text/css">' +
20101 'IMG { cursor: pointer } ' +
20105 return '<html><head>' + st +
20106 //<style type="text/css">' +
20107 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20109 ' </head><body class="roo-htmleditor-body"></body></html>';
20113 onRender : function(ct, position)
20116 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20117 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20120 this.el.dom.style.border = '0 none';
20121 this.el.dom.setAttribute('tabIndex', -1);
20122 this.el.addClass('x-hidden hide');
20126 if(Roo.isIE){ // fix IE 1px bogus margin
20127 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20131 this.frameId = Roo.id();
20135 var iframe = this.owner.wrap.createChild({
20137 cls: 'form-control', // bootstrap..
20139 name: this.frameId,
20140 frameBorder : 'no',
20141 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20146 this.iframe = iframe.dom;
20148 this.assignDocWin();
20150 this.doc.designMode = 'on';
20153 this.doc.write(this.getDocMarkup());
20157 var task = { // must defer to wait for browser to be ready
20159 //console.log("run task?" + this.doc.readyState);
20160 this.assignDocWin();
20161 if(this.doc.body || this.doc.readyState == 'complete'){
20163 this.doc.designMode="on";
20167 Roo.TaskMgr.stop(task);
20168 this.initEditor.defer(10, this);
20175 Roo.TaskMgr.start(task);
20180 onResize : function(w, h)
20182 Roo.log('resize: ' +w + ',' + h );
20183 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20187 if(typeof w == 'number'){
20189 this.iframe.style.width = w + 'px';
20191 if(typeof h == 'number'){
20193 this.iframe.style.height = h + 'px';
20195 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20202 * Toggles the editor between standard and source edit mode.
20203 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20205 toggleSourceEdit : function(sourceEditMode){
20207 this.sourceEditMode = sourceEditMode === true;
20209 if(this.sourceEditMode){
20211 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20214 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20215 //this.iframe.className = '';
20218 //this.setSize(this.owner.wrap.getSize());
20219 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20226 * Protected method that will not generally be called directly. If you need/want
20227 * custom HTML cleanup, this is the method you should override.
20228 * @param {String} html The HTML to be cleaned
20229 * return {String} The cleaned HTML
20231 cleanHtml : function(html){
20232 html = String(html);
20233 if(html.length > 5){
20234 if(Roo.isSafari){ // strip safari nonsense
20235 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20238 if(html == ' '){
20245 * HTML Editor -> Textarea
20246 * Protected method that will not generally be called directly. Syncs the contents
20247 * of the editor iframe with the textarea.
20249 syncValue : function(){
20250 if(this.initialized){
20251 var bd = (this.doc.body || this.doc.documentElement);
20252 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20253 var html = bd.innerHTML;
20255 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20256 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20258 html = '<div style="'+m[0]+'">' + html + '</div>';
20261 html = this.cleanHtml(html);
20262 // fix up the special chars.. normaly like back quotes in word...
20263 // however we do not want to do this with chinese..
20264 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20265 var cc = b.charCodeAt();
20267 (cc >= 0x4E00 && cc < 0xA000 ) ||
20268 (cc >= 0x3400 && cc < 0x4E00 ) ||
20269 (cc >= 0xf900 && cc < 0xfb00 )
20275 if(this.owner.fireEvent('beforesync', this, html) !== false){
20276 this.el.dom.value = html;
20277 this.owner.fireEvent('sync', this, html);
20283 * Protected method that will not generally be called directly. Pushes the value of the textarea
20284 * into the iframe editor.
20286 pushValue : function(){
20287 if(this.initialized){
20288 var v = this.el.dom.value.trim();
20290 // if(v.length < 1){
20294 if(this.owner.fireEvent('beforepush', this, v) !== false){
20295 var d = (this.doc.body || this.doc.documentElement);
20297 this.cleanUpPaste();
20298 this.el.dom.value = d.innerHTML;
20299 this.owner.fireEvent('push', this, v);
20305 deferFocus : function(){
20306 this.focus.defer(10, this);
20310 focus : function(){
20311 if(this.win && !this.sourceEditMode){
20318 assignDocWin: function()
20320 var iframe = this.iframe;
20323 this.doc = iframe.contentWindow.document;
20324 this.win = iframe.contentWindow;
20326 // if (!Roo.get(this.frameId)) {
20329 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20330 // this.win = Roo.get(this.frameId).dom.contentWindow;
20332 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20336 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20337 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20342 initEditor : function(){
20343 //console.log("INIT EDITOR");
20344 this.assignDocWin();
20348 this.doc.designMode="on";
20350 this.doc.write(this.getDocMarkup());
20353 var dbody = (this.doc.body || this.doc.documentElement);
20354 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20355 // this copies styles from the containing element into thsi one..
20356 // not sure why we need all of this..
20357 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20359 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20360 //ss['background-attachment'] = 'fixed'; // w3c
20361 dbody.bgProperties = 'fixed'; // ie
20362 //Roo.DomHelper.applyStyles(dbody, ss);
20363 Roo.EventManager.on(this.doc, {
20364 //'mousedown': this.onEditorEvent,
20365 'mouseup': this.onEditorEvent,
20366 'dblclick': this.onEditorEvent,
20367 'click': this.onEditorEvent,
20368 'keyup': this.onEditorEvent,
20373 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20375 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20376 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20378 this.initialized = true;
20380 this.owner.fireEvent('initialize', this);
20385 onDestroy : function(){
20391 //for (var i =0; i < this.toolbars.length;i++) {
20392 // // fixme - ask toolbars for heights?
20393 // this.toolbars[i].onDestroy();
20396 //this.wrap.dom.innerHTML = '';
20397 //this.wrap.remove();
20402 onFirstFocus : function(){
20404 this.assignDocWin();
20407 this.activated = true;
20410 if(Roo.isGecko){ // prevent silly gecko errors
20412 var s = this.win.getSelection();
20413 if(!s.focusNode || s.focusNode.nodeType != 3){
20414 var r = s.getRangeAt(0);
20415 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20420 this.execCmd('useCSS', true);
20421 this.execCmd('styleWithCSS', false);
20424 this.owner.fireEvent('activate', this);
20428 adjustFont: function(btn){
20429 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20430 //if(Roo.isSafari){ // safari
20433 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20434 if(Roo.isSafari){ // safari
20435 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20436 v = (v < 10) ? 10 : v;
20437 v = (v > 48) ? 48 : v;
20438 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20443 v = Math.max(1, v+adjust);
20445 this.execCmd('FontSize', v );
20448 onEditorEvent : function(e)
20450 this.owner.fireEvent('editorevent', this, e);
20451 // this.updateToolbar();
20452 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20455 insertTag : function(tg)
20457 // could be a bit smarter... -> wrap the current selected tRoo..
20458 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20460 range = this.createRange(this.getSelection());
20461 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20462 wrappingNode.appendChild(range.extractContents());
20463 range.insertNode(wrappingNode);
20470 this.execCmd("formatblock", tg);
20474 insertText : function(txt)
20478 var range = this.createRange();
20479 range.deleteContents();
20480 //alert(Sender.getAttribute('label'));
20482 range.insertNode(this.doc.createTextNode(txt));
20488 * Executes a Midas editor command on the editor document and performs necessary focus and
20489 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20490 * @param {String} cmd The Midas command
20491 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20493 relayCmd : function(cmd, value){
20495 this.execCmd(cmd, value);
20496 this.owner.fireEvent('editorevent', this);
20497 //this.updateToolbar();
20498 this.owner.deferFocus();
20502 * Executes a Midas editor command directly on the editor document.
20503 * For visual commands, you should use {@link #relayCmd} instead.
20504 * <b>This should only be called after the editor is initialized.</b>
20505 * @param {String} cmd The Midas command
20506 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20508 execCmd : function(cmd, value){
20509 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20516 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20518 * @param {String} text | dom node..
20520 insertAtCursor : function(text)
20525 if(!this.activated){
20531 var r = this.doc.selection.createRange();
20542 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20546 // from jquery ui (MIT licenced)
20548 var win = this.win;
20550 if (win.getSelection && win.getSelection().getRangeAt) {
20551 range = win.getSelection().getRangeAt(0);
20552 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20553 range.insertNode(node);
20554 } else if (win.document.selection && win.document.selection.createRange) {
20555 // no firefox support
20556 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20557 win.document.selection.createRange().pasteHTML(txt);
20559 // no firefox support
20560 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20561 this.execCmd('InsertHTML', txt);
20570 mozKeyPress : function(e){
20572 var c = e.getCharCode(), cmd;
20575 c = String.fromCharCode(c).toLowerCase();
20589 this.cleanUpPaste.defer(100, this);
20597 e.preventDefault();
20605 fixKeys : function(){ // load time branching for fastest keydown performance
20607 return function(e){
20608 var k = e.getKey(), r;
20611 r = this.doc.selection.createRange();
20614 r.pasteHTML('    ');
20621 r = this.doc.selection.createRange();
20623 var target = r.parentElement();
20624 if(!target || target.tagName.toLowerCase() != 'li'){
20626 r.pasteHTML('<br />');
20632 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20633 this.cleanUpPaste.defer(100, this);
20639 }else if(Roo.isOpera){
20640 return function(e){
20641 var k = e.getKey();
20645 this.execCmd('InsertHTML','    ');
20648 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20649 this.cleanUpPaste.defer(100, this);
20654 }else if(Roo.isSafari){
20655 return function(e){
20656 var k = e.getKey();
20660 this.execCmd('InsertText','\t');
20664 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20665 this.cleanUpPaste.defer(100, this);
20673 getAllAncestors: function()
20675 var p = this.getSelectedNode();
20678 a.push(p); // push blank onto stack..
20679 p = this.getParentElement();
20683 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20687 a.push(this.doc.body);
20691 lastSelNode : false,
20694 getSelection : function()
20696 this.assignDocWin();
20697 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20700 getSelectedNode: function()
20702 // this may only work on Gecko!!!
20704 // should we cache this!!!!
20709 var range = this.createRange(this.getSelection()).cloneRange();
20712 var parent = range.parentElement();
20714 var testRange = range.duplicate();
20715 testRange.moveToElementText(parent);
20716 if (testRange.inRange(range)) {
20719 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20722 parent = parent.parentElement;
20727 // is ancestor a text element.
20728 var ac = range.commonAncestorContainer;
20729 if (ac.nodeType == 3) {
20730 ac = ac.parentNode;
20733 var ar = ac.childNodes;
20736 var other_nodes = [];
20737 var has_other_nodes = false;
20738 for (var i=0;i<ar.length;i++) {
20739 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20742 // fullly contained node.
20744 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20749 // probably selected..
20750 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20751 other_nodes.push(ar[i]);
20755 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20760 has_other_nodes = true;
20762 if (!nodes.length && other_nodes.length) {
20763 nodes= other_nodes;
20765 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20771 createRange: function(sel)
20773 // this has strange effects when using with
20774 // top toolbar - not sure if it's a great idea.
20775 //this.editor.contentWindow.focus();
20776 if (typeof sel != "undefined") {
20778 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20780 return this.doc.createRange();
20783 return this.doc.createRange();
20786 getParentElement: function()
20789 this.assignDocWin();
20790 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20792 var range = this.createRange(sel);
20795 var p = range.commonAncestorContainer;
20796 while (p.nodeType == 3) { // text node
20807 * Range intersection.. the hard stuff...
20811 * [ -- selected range --- ]
20815 * if end is before start or hits it. fail.
20816 * if start is after end or hits it fail.
20818 * if either hits (but other is outside. - then it's not
20824 // @see http://www.thismuchiknow.co.uk/?p=64.
20825 rangeIntersectsNode : function(range, node)
20827 var nodeRange = node.ownerDocument.createRange();
20829 nodeRange.selectNode(node);
20831 nodeRange.selectNodeContents(node);
20834 var rangeStartRange = range.cloneRange();
20835 rangeStartRange.collapse(true);
20837 var rangeEndRange = range.cloneRange();
20838 rangeEndRange.collapse(false);
20840 var nodeStartRange = nodeRange.cloneRange();
20841 nodeStartRange.collapse(true);
20843 var nodeEndRange = nodeRange.cloneRange();
20844 nodeEndRange.collapse(false);
20846 return rangeStartRange.compareBoundaryPoints(
20847 Range.START_TO_START, nodeEndRange) == -1 &&
20848 rangeEndRange.compareBoundaryPoints(
20849 Range.START_TO_START, nodeStartRange) == 1;
20853 rangeCompareNode : function(range, node)
20855 var nodeRange = node.ownerDocument.createRange();
20857 nodeRange.selectNode(node);
20859 nodeRange.selectNodeContents(node);
20863 range.collapse(true);
20865 nodeRange.collapse(true);
20867 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20868 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20870 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20872 var nodeIsBefore = ss == 1;
20873 var nodeIsAfter = ee == -1;
20875 if (nodeIsBefore && nodeIsAfter) {
20878 if (!nodeIsBefore && nodeIsAfter) {
20879 return 1; //right trailed.
20882 if (nodeIsBefore && !nodeIsAfter) {
20883 return 2; // left trailed.
20889 // private? - in a new class?
20890 cleanUpPaste : function()
20892 // cleans up the whole document..
20893 Roo.log('cleanuppaste');
20895 this.cleanUpChildren(this.doc.body);
20896 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20897 if (clean != this.doc.body.innerHTML) {
20898 this.doc.body.innerHTML = clean;
20903 cleanWordChars : function(input) {// change the chars to hex code
20904 var he = Roo.HtmlEditorCore;
20906 var output = input;
20907 Roo.each(he.swapCodes, function(sw) {
20908 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20910 output = output.replace(swapper, sw[1]);
20917 cleanUpChildren : function (n)
20919 if (!n.childNodes.length) {
20922 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20923 this.cleanUpChild(n.childNodes[i]);
20930 cleanUpChild : function (node)
20933 //console.log(node);
20934 if (node.nodeName == "#text") {
20935 // clean up silly Windows -- stuff?
20938 if (node.nodeName == "#comment") {
20939 node.parentNode.removeChild(node);
20940 // clean up silly Windows -- stuff?
20943 var lcname = node.tagName.toLowerCase();
20944 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20945 // whitelist of tags..
20947 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20949 node.parentNode.removeChild(node);
20954 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20956 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20957 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20959 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20960 // remove_keep_children = true;
20963 if (remove_keep_children) {
20964 this.cleanUpChildren(node);
20965 // inserts everything just before this node...
20966 while (node.childNodes.length) {
20967 var cn = node.childNodes[0];
20968 node.removeChild(cn);
20969 node.parentNode.insertBefore(cn, node);
20971 node.parentNode.removeChild(node);
20975 if (!node.attributes || !node.attributes.length) {
20976 this.cleanUpChildren(node);
20980 function cleanAttr(n,v)
20983 if (v.match(/^\./) || v.match(/^\//)) {
20986 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20989 if (v.match(/^#/)) {
20992 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20993 node.removeAttribute(n);
20997 var cwhite = this.cwhite;
20998 var cblack = this.cblack;
21000 function cleanStyle(n,v)
21002 if (v.match(/expression/)) { //XSS?? should we even bother..
21003 node.removeAttribute(n);
21007 var parts = v.split(/;/);
21010 Roo.each(parts, function(p) {
21011 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21015 var l = p.split(':').shift().replace(/\s+/g,'');
21016 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21018 if ( cwhite.length && cblack.indexOf(l) > -1) {
21019 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21020 //node.removeAttribute(n);
21024 // only allow 'c whitelisted system attributes'
21025 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21026 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21027 //node.removeAttribute(n);
21037 if (clean.length) {
21038 node.setAttribute(n, clean.join(';'));
21040 node.removeAttribute(n);
21046 for (var i = node.attributes.length-1; i > -1 ; i--) {
21047 var a = node.attributes[i];
21050 if (a.name.toLowerCase().substr(0,2)=='on') {
21051 node.removeAttribute(a.name);
21054 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21055 node.removeAttribute(a.name);
21058 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21059 cleanAttr(a.name,a.value); // fixme..
21062 if (a.name == 'style') {
21063 cleanStyle(a.name,a.value);
21066 /// clean up MS crap..
21067 // tecnically this should be a list of valid class'es..
21070 if (a.name == 'class') {
21071 if (a.value.match(/^Mso/)) {
21072 node.className = '';
21075 if (a.value.match(/body/)) {
21076 node.className = '';
21087 this.cleanUpChildren(node);
21093 * Clean up MS wordisms...
21095 cleanWord : function(node)
21100 this.cleanWord(this.doc.body);
21103 if (node.nodeName == "#text") {
21104 // clean up silly Windows -- stuff?
21107 if (node.nodeName == "#comment") {
21108 node.parentNode.removeChild(node);
21109 // clean up silly Windows -- stuff?
21113 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21114 node.parentNode.removeChild(node);
21118 // remove - but keep children..
21119 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21120 while (node.childNodes.length) {
21121 var cn = node.childNodes[0];
21122 node.removeChild(cn);
21123 node.parentNode.insertBefore(cn, node);
21125 node.parentNode.removeChild(node);
21126 this.iterateChildren(node, this.cleanWord);
21130 if (node.className.length) {
21132 var cn = node.className.split(/\W+/);
21134 Roo.each(cn, function(cls) {
21135 if (cls.match(/Mso[a-zA-Z]+/)) {
21140 node.className = cna.length ? cna.join(' ') : '';
21142 node.removeAttribute("class");
21146 if (node.hasAttribute("lang")) {
21147 node.removeAttribute("lang");
21150 if (node.hasAttribute("style")) {
21152 var styles = node.getAttribute("style").split(";");
21154 Roo.each(styles, function(s) {
21155 if (!s.match(/:/)) {
21158 var kv = s.split(":");
21159 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21162 // what ever is left... we allow.
21165 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21166 if (!nstyle.length) {
21167 node.removeAttribute('style');
21170 this.iterateChildren(node, this.cleanWord);
21176 * iterateChildren of a Node, calling fn each time, using this as the scole..
21177 * @param {DomNode} node node to iterate children of.
21178 * @param {Function} fn method of this class to call on each item.
21180 iterateChildren : function(node, fn)
21182 if (!node.childNodes.length) {
21185 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21186 fn.call(this, node.childNodes[i])
21192 * cleanTableWidths.
21194 * Quite often pasting from word etc.. results in tables with column and widths.
21195 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21198 cleanTableWidths : function(node)
21203 this.cleanTableWidths(this.doc.body);
21208 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21211 Roo.log(node.tagName);
21212 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21213 this.iterateChildren(node, this.cleanTableWidths);
21216 if (node.hasAttribute('width')) {
21217 node.removeAttribute('width');
21221 if (node.hasAttribute("style")) {
21224 var styles = node.getAttribute("style").split(";");
21226 Roo.each(styles, function(s) {
21227 if (!s.match(/:/)) {
21230 var kv = s.split(":");
21231 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21234 // what ever is left... we allow.
21237 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21238 if (!nstyle.length) {
21239 node.removeAttribute('style');
21243 this.iterateChildren(node, this.cleanTableWidths);
21251 domToHTML : function(currentElement, depth, nopadtext) {
21253 depth = depth || 0;
21254 nopadtext = nopadtext || false;
21256 if (!currentElement) {
21257 return this.domToHTML(this.doc.body);
21260 //Roo.log(currentElement);
21262 var allText = false;
21263 var nodeName = currentElement.nodeName;
21264 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21266 if (nodeName == '#text') {
21268 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21273 if (nodeName != 'BODY') {
21276 // Prints the node tagName, such as <A>, <IMG>, etc
21279 for(i = 0; i < currentElement.attributes.length;i++) {
21281 var aname = currentElement.attributes.item(i).name;
21282 if (!currentElement.attributes.item(i).value.length) {
21285 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21288 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21297 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21300 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21305 // Traverse the tree
21307 var currentElementChild = currentElement.childNodes.item(i);
21308 var allText = true;
21309 var innerHTML = '';
21311 while (currentElementChild) {
21312 // Formatting code (indent the tree so it looks nice on the screen)
21313 var nopad = nopadtext;
21314 if (lastnode == 'SPAN') {
21318 if (currentElementChild.nodeName == '#text') {
21319 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21320 toadd = nopadtext ? toadd : toadd.trim();
21321 if (!nopad && toadd.length > 80) {
21322 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21324 innerHTML += toadd;
21327 currentElementChild = currentElement.childNodes.item(i);
21333 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21335 // Recursively traverse the tree structure of the child node
21336 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21337 lastnode = currentElementChild.nodeName;
21339 currentElementChild=currentElement.childNodes.item(i);
21345 // The remaining code is mostly for formatting the tree
21346 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21351 ret+= "</"+tagName+">";
21357 applyBlacklists : function()
21359 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21360 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21364 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21365 if (b.indexOf(tag) > -1) {
21368 this.white.push(tag);
21372 Roo.each(w, function(tag) {
21373 if (b.indexOf(tag) > -1) {
21376 if (this.white.indexOf(tag) > -1) {
21379 this.white.push(tag);
21384 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21385 if (w.indexOf(tag) > -1) {
21388 this.black.push(tag);
21392 Roo.each(b, function(tag) {
21393 if (w.indexOf(tag) > -1) {
21396 if (this.black.indexOf(tag) > -1) {
21399 this.black.push(tag);
21404 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21405 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21409 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21410 if (b.indexOf(tag) > -1) {
21413 this.cwhite.push(tag);
21417 Roo.each(w, function(tag) {
21418 if (b.indexOf(tag) > -1) {
21421 if (this.cwhite.indexOf(tag) > -1) {
21424 this.cwhite.push(tag);
21429 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21430 if (w.indexOf(tag) > -1) {
21433 this.cblack.push(tag);
21437 Roo.each(b, function(tag) {
21438 if (w.indexOf(tag) > -1) {
21441 if (this.cblack.indexOf(tag) > -1) {
21444 this.cblack.push(tag);
21449 setStylesheets : function(stylesheets)
21451 if(typeof(stylesheets) == 'string'){
21452 Roo.get(this.iframe.contentDocument.head).createChild({
21454 rel : 'stylesheet',
21463 Roo.each(stylesheets, function(s) {
21468 Roo.get(_this.iframe.contentDocument.head).createChild({
21470 rel : 'stylesheet',
21479 removeStylesheets : function()
21483 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21488 // hide stuff that is not compatible
21502 * @event specialkey
21506 * @cfg {String} fieldClass @hide
21509 * @cfg {String} focusClass @hide
21512 * @cfg {String} autoCreate @hide
21515 * @cfg {String} inputType @hide
21518 * @cfg {String} invalidClass @hide
21521 * @cfg {String} invalidText @hide
21524 * @cfg {String} msgFx @hide
21527 * @cfg {String} validateOnBlur @hide
21531 Roo.HtmlEditorCore.white = [
21532 'area', 'br', 'img', 'input', 'hr', 'wbr',
21534 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21535 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21536 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21537 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21538 'table', 'ul', 'xmp',
21540 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21543 'dir', 'menu', 'ol', 'ul', 'dl',
21549 Roo.HtmlEditorCore.black = [
21550 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21552 'base', 'basefont', 'bgsound', 'blink', 'body',
21553 'frame', 'frameset', 'head', 'html', 'ilayer',
21554 'iframe', 'layer', 'link', 'meta', 'object',
21555 'script', 'style' ,'title', 'xml' // clean later..
21557 Roo.HtmlEditorCore.clean = [
21558 'script', 'style', 'title', 'xml'
21560 Roo.HtmlEditorCore.remove = [
21565 Roo.HtmlEditorCore.ablack = [
21569 Roo.HtmlEditorCore.aclean = [
21570 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21574 Roo.HtmlEditorCore.pwhite= [
21575 'http', 'https', 'mailto'
21578 // white listed style attributes.
21579 Roo.HtmlEditorCore.cwhite= [
21580 // 'text-align', /// default is to allow most things..
21586 // black listed style attributes.
21587 Roo.HtmlEditorCore.cblack= [
21588 // 'font-size' -- this can be set by the project
21592 Roo.HtmlEditorCore.swapCodes =[
21611 * @class Roo.bootstrap.HtmlEditor
21612 * @extends Roo.bootstrap.TextArea
21613 * Bootstrap HtmlEditor class
21616 * Create a new HtmlEditor
21617 * @param {Object} config The config object
21620 Roo.bootstrap.HtmlEditor = function(config){
21621 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21622 if (!this.toolbars) {
21623 this.toolbars = [];
21625 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21628 * @event initialize
21629 * Fires when the editor is fully initialized (including the iframe)
21630 * @param {HtmlEditor} this
21635 * Fires when the editor is first receives the focus. Any insertion must wait
21636 * until after this event.
21637 * @param {HtmlEditor} this
21641 * @event beforesync
21642 * Fires before the textarea is updated with content from the editor iframe. Return false
21643 * to cancel the sync.
21644 * @param {HtmlEditor} this
21645 * @param {String} html
21649 * @event beforepush
21650 * Fires before the iframe editor is updated with content from the textarea. Return false
21651 * to cancel the push.
21652 * @param {HtmlEditor} this
21653 * @param {String} html
21658 * Fires when the textarea is updated with content from the editor iframe.
21659 * @param {HtmlEditor} this
21660 * @param {String} html
21665 * Fires when the iframe editor is updated with content from the textarea.
21666 * @param {HtmlEditor} this
21667 * @param {String} html
21671 * @event editmodechange
21672 * Fires when the editor switches edit modes
21673 * @param {HtmlEditor} this
21674 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21676 editmodechange: true,
21678 * @event editorevent
21679 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21680 * @param {HtmlEditor} this
21684 * @event firstfocus
21685 * Fires when on first focus - needed by toolbars..
21686 * @param {HtmlEditor} this
21691 * Auto save the htmlEditor value as a file into Events
21692 * @param {HtmlEditor} this
21696 * @event savedpreview
21697 * preview the saved version of htmlEditor
21698 * @param {HtmlEditor} this
21705 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21709 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21714 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21719 * @cfg {Number} height (in pixels)
21723 * @cfg {Number} width (in pixels)
21728 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21731 stylesheets: false,
21736 // private properties
21737 validationEvent : false,
21739 initialized : false,
21742 onFocus : Roo.emptyFn,
21744 hideMode:'offsets',
21747 tbContainer : false,
21749 toolbarContainer :function() {
21750 return this.wrap.select('.x-html-editor-tb',true).first();
21754 * Protected method that will not generally be called directly. It
21755 * is called when the editor creates its toolbar. Override this method if you need to
21756 * add custom toolbar buttons.
21757 * @param {HtmlEditor} editor
21759 createToolbar : function(){
21761 Roo.log("create toolbars");
21763 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21764 this.toolbars[0].render(this.toolbarContainer());
21768 // if (!editor.toolbars || !editor.toolbars.length) {
21769 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21772 // for (var i =0 ; i < editor.toolbars.length;i++) {
21773 // editor.toolbars[i] = Roo.factory(
21774 // typeof(editor.toolbars[i]) == 'string' ?
21775 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21776 // Roo.bootstrap.HtmlEditor);
21777 // editor.toolbars[i].init(editor);
21783 onRender : function(ct, position)
21785 // Roo.log("Call onRender: " + this.xtype);
21787 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21789 this.wrap = this.inputEl().wrap({
21790 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21793 this.editorcore.onRender(ct, position);
21795 if (this.resizable) {
21796 this.resizeEl = new Roo.Resizable(this.wrap, {
21800 minHeight : this.height,
21801 height: this.height,
21802 handles : this.resizable,
21805 resize : function(r, w, h) {
21806 _t.onResize(w,h); // -something
21812 this.createToolbar(this);
21815 if(!this.width && this.resizable){
21816 this.setSize(this.wrap.getSize());
21818 if (this.resizeEl) {
21819 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21820 // should trigger onReize..
21826 onResize : function(w, h)
21828 Roo.log('resize: ' +w + ',' + h );
21829 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21833 if(this.inputEl() ){
21834 if(typeof w == 'number'){
21835 var aw = w - this.wrap.getFrameWidth('lr');
21836 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21839 if(typeof h == 'number'){
21840 var tbh = -11; // fixme it needs to tool bar size!
21841 for (var i =0; i < this.toolbars.length;i++) {
21842 // fixme - ask toolbars for heights?
21843 tbh += this.toolbars[i].el.getHeight();
21844 //if (this.toolbars[i].footer) {
21845 // tbh += this.toolbars[i].footer.el.getHeight();
21853 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21854 ah -= 5; // knock a few pixes off for look..
21855 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21859 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21860 this.editorcore.onResize(ew,eh);
21865 * Toggles the editor between standard and source edit mode.
21866 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21868 toggleSourceEdit : function(sourceEditMode)
21870 this.editorcore.toggleSourceEdit(sourceEditMode);
21872 if(this.editorcore.sourceEditMode){
21873 Roo.log('editor - showing textarea');
21876 // Roo.log(this.syncValue());
21878 this.inputEl().removeClass(['hide', 'x-hidden']);
21879 this.inputEl().dom.removeAttribute('tabIndex');
21880 this.inputEl().focus();
21882 Roo.log('editor - hiding textarea');
21884 // Roo.log(this.pushValue());
21887 this.inputEl().addClass(['hide', 'x-hidden']);
21888 this.inputEl().dom.setAttribute('tabIndex', -1);
21889 //this.deferFocus();
21892 if(this.resizable){
21893 this.setSize(this.wrap.getSize());
21896 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21899 // private (for BoxComponent)
21900 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21902 // private (for BoxComponent)
21903 getResizeEl : function(){
21907 // private (for BoxComponent)
21908 getPositionEl : function(){
21913 initEvents : function(){
21914 this.originalValue = this.getValue();
21918 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21921 // markInvalid : Roo.emptyFn,
21923 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21926 // clearInvalid : Roo.emptyFn,
21928 setValue : function(v){
21929 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21930 this.editorcore.pushValue();
21935 deferFocus : function(){
21936 this.focus.defer(10, this);
21940 focus : function(){
21941 this.editorcore.focus();
21947 onDestroy : function(){
21953 for (var i =0; i < this.toolbars.length;i++) {
21954 // fixme - ask toolbars for heights?
21955 this.toolbars[i].onDestroy();
21958 this.wrap.dom.innerHTML = '';
21959 this.wrap.remove();
21964 onFirstFocus : function(){
21965 //Roo.log("onFirstFocus");
21966 this.editorcore.onFirstFocus();
21967 for (var i =0; i < this.toolbars.length;i++) {
21968 this.toolbars[i].onFirstFocus();
21974 syncValue : function()
21976 this.editorcore.syncValue();
21979 pushValue : function()
21981 this.editorcore.pushValue();
21985 // hide stuff that is not compatible
21999 * @event specialkey
22003 * @cfg {String} fieldClass @hide
22006 * @cfg {String} focusClass @hide
22009 * @cfg {String} autoCreate @hide
22012 * @cfg {String} inputType @hide
22015 * @cfg {String} invalidClass @hide
22018 * @cfg {String} invalidText @hide
22021 * @cfg {String} msgFx @hide
22024 * @cfg {String} validateOnBlur @hide
22033 Roo.namespace('Roo.bootstrap.htmleditor');
22035 * @class Roo.bootstrap.HtmlEditorToolbar1
22040 new Roo.bootstrap.HtmlEditor({
22043 new Roo.bootstrap.HtmlEditorToolbar1({
22044 disable : { fonts: 1 , format: 1, ..., ... , ...],
22050 * @cfg {Object} disable List of elements to disable..
22051 * @cfg {Array} btns List of additional buttons.
22055 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22058 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22061 Roo.apply(this, config);
22063 // default disabled, based on 'good practice'..
22064 this.disable = this.disable || {};
22065 Roo.applyIf(this.disable, {
22068 specialElements : true
22070 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22072 this.editor = config.editor;
22073 this.editorcore = config.editor.editorcore;
22075 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22077 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22078 // dont call parent... till later.
22080 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22085 editorcore : false,
22090 "h1","h2","h3","h4","h5","h6",
22092 "abbr", "acronym", "address", "cite", "samp", "var",
22096 onRender : function(ct, position)
22098 // Roo.log("Call onRender: " + this.xtype);
22100 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22102 this.el.dom.style.marginBottom = '0';
22104 var editorcore = this.editorcore;
22105 var editor= this.editor;
22108 var btn = function(id,cmd , toggle, handler){
22110 var event = toggle ? 'toggle' : 'click';
22115 xns: Roo.bootstrap,
22118 enableToggle:toggle !== false,
22120 pressed : toggle ? false : null,
22123 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22124 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22133 xns: Roo.bootstrap,
22134 glyphicon : 'font',
22138 xns: Roo.bootstrap,
22142 Roo.each(this.formats, function(f) {
22143 style.menu.items.push({
22145 xns: Roo.bootstrap,
22146 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22151 editorcore.insertTag(this.tagname);
22158 children.push(style);
22161 btn('bold',false,true);
22162 btn('italic',false,true);
22163 btn('align-left', 'justifyleft',true);
22164 btn('align-center', 'justifycenter',true);
22165 btn('align-right' , 'justifyright',true);
22166 btn('link', false, false, function(btn) {
22167 //Roo.log("create link?");
22168 var url = prompt(this.createLinkText, this.defaultLinkValue);
22169 if(url && url != 'http:/'+'/'){
22170 this.editorcore.relayCmd('createlink', url);
22173 btn('list','insertunorderedlist',true);
22174 btn('pencil', false,true, function(btn){
22177 this.toggleSourceEdit(btn.pressed);
22183 xns: Roo.bootstrap,
22188 xns: Roo.bootstrap,
22193 cog.menu.items.push({
22195 xns: Roo.bootstrap,
22196 html : Clean styles,
22201 editorcore.insertTag(this.tagname);
22210 this.xtype = 'NavSimplebar';
22212 for(var i=0;i< children.length;i++) {
22214 this.buttons.add(this.addxtypeChild(children[i]));
22218 editor.on('editorevent', this.updateToolbar, this);
22220 onBtnClick : function(id)
22222 this.editorcore.relayCmd(id);
22223 this.editorcore.focus();
22227 * Protected method that will not generally be called directly. It triggers
22228 * a toolbar update by reading the markup state of the current selection in the editor.
22230 updateToolbar: function(){
22232 if(!this.editorcore.activated){
22233 this.editor.onFirstFocus(); // is this neeed?
22237 var btns = this.buttons;
22238 var doc = this.editorcore.doc;
22239 btns.get('bold').setActive(doc.queryCommandState('bold'));
22240 btns.get('italic').setActive(doc.queryCommandState('italic'));
22241 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22243 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22244 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22245 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22247 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22248 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22251 var ans = this.editorcore.getAllAncestors();
22252 if (this.formatCombo) {
22255 var store = this.formatCombo.store;
22256 this.formatCombo.setValue("");
22257 for (var i =0; i < ans.length;i++) {
22258 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22260 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22268 // hides menus... - so this cant be on a menu...
22269 Roo.bootstrap.MenuMgr.hideAll();
22271 Roo.bootstrap.MenuMgr.hideAll();
22272 //this.editorsyncValue();
22274 onFirstFocus: function() {
22275 this.buttons.each(function(item){
22279 toggleSourceEdit : function(sourceEditMode){
22282 if(sourceEditMode){
22283 Roo.log("disabling buttons");
22284 this.buttons.each( function(item){
22285 if(item.cmd != 'pencil'){
22291 Roo.log("enabling buttons");
22292 if(this.editorcore.initialized){
22293 this.buttons.each( function(item){
22299 Roo.log("calling toggole on editor");
22300 // tell the editor that it's been pressed..
22301 this.editor.toggleSourceEdit(sourceEditMode);
22311 * @class Roo.bootstrap.Table.AbstractSelectionModel
22312 * @extends Roo.util.Observable
22313 * Abstract base class for grid SelectionModels. It provides the interface that should be
22314 * implemented by descendant classes. This class should not be directly instantiated.
22317 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22318 this.locked = false;
22319 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22323 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22324 /** @ignore Called by the grid automatically. Do not call directly. */
22325 init : function(grid){
22331 * Locks the selections.
22334 this.locked = true;
22338 * Unlocks the selections.
22340 unlock : function(){
22341 this.locked = false;
22345 * Returns true if the selections are locked.
22346 * @return {Boolean}
22348 isLocked : function(){
22349 return this.locked;
22353 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22354 * @class Roo.bootstrap.Table.RowSelectionModel
22355 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22356 * It supports multiple selections and keyboard selection/navigation.
22358 * @param {Object} config
22361 Roo.bootstrap.Table.RowSelectionModel = function(config){
22362 Roo.apply(this, config);
22363 this.selections = new Roo.util.MixedCollection(false, function(o){
22368 this.lastActive = false;
22372 * @event selectionchange
22373 * Fires when the selection changes
22374 * @param {SelectionModel} this
22376 "selectionchange" : true,
22378 * @event afterselectionchange
22379 * Fires after the selection changes (eg. by key press or clicking)
22380 * @param {SelectionModel} this
22382 "afterselectionchange" : true,
22384 * @event beforerowselect
22385 * Fires when a row is selected being selected, return false to cancel.
22386 * @param {SelectionModel} this
22387 * @param {Number} rowIndex The selected index
22388 * @param {Boolean} keepExisting False if other selections will be cleared
22390 "beforerowselect" : true,
22393 * Fires when a row is selected.
22394 * @param {SelectionModel} this
22395 * @param {Number} rowIndex The selected index
22396 * @param {Roo.data.Record} r The record
22398 "rowselect" : true,
22400 * @event rowdeselect
22401 * Fires when a row is deselected.
22402 * @param {SelectionModel} this
22403 * @param {Number} rowIndex The selected index
22405 "rowdeselect" : true
22407 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22408 this.locked = false;
22411 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22413 * @cfg {Boolean} singleSelect
22414 * True to allow selection of only one row at a time (defaults to false)
22416 singleSelect : false,
22419 initEvents : function()
22422 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22423 this.grid.on("mousedown", this.handleMouseDown, this);
22424 }else{ // allow click to work like normal
22425 this.grid.on("rowclick", this.handleDragableRowClick, this);
22428 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22429 "up" : function(e){
22431 this.selectPrevious(e.shiftKey);
22432 }else if(this.last !== false && this.lastActive !== false){
22433 var last = this.last;
22434 this.selectRange(this.last, this.lastActive-1);
22435 this.grid.getView().focusRow(this.lastActive);
22436 if(last !== false){
22440 this.selectFirstRow();
22442 this.fireEvent("afterselectionchange", this);
22444 "down" : function(e){
22446 this.selectNext(e.shiftKey);
22447 }else if(this.last !== false && this.lastActive !== false){
22448 var last = this.last;
22449 this.selectRange(this.last, this.lastActive+1);
22450 this.grid.getView().focusRow(this.lastActive);
22451 if(last !== false){
22455 this.selectFirstRow();
22457 this.fireEvent("afterselectionchange", this);
22462 var view = this.grid.view;
22463 view.on("refresh", this.onRefresh, this);
22464 view.on("rowupdated", this.onRowUpdated, this);
22465 view.on("rowremoved", this.onRemove, this);
22470 onRefresh : function(){
22471 var ds = this.grid.dataSource, i, v = this.grid.view;
22472 var s = this.selections;
22473 s.each(function(r){
22474 if((i = ds.indexOfId(r.id)) != -1){
22483 onRemove : function(v, index, r){
22484 this.selections.remove(r);
22488 onRowUpdated : function(v, index, r){
22489 if(this.isSelected(r)){
22490 v.onRowSelect(index);
22496 * @param {Array} records The records to select
22497 * @param {Boolean} keepExisting (optional) True to keep existing selections
22499 selectRecords : function(records, keepExisting){
22501 this.clearSelections();
22503 var ds = this.grid.dataSource;
22504 for(var i = 0, len = records.length; i < len; i++){
22505 this.selectRow(ds.indexOf(records[i]), true);
22510 * Gets the number of selected rows.
22513 getCount : function(){
22514 return this.selections.length;
22518 * Selects the first row in the grid.
22520 selectFirstRow : function(){
22525 * Select the last row.
22526 * @param {Boolean} keepExisting (optional) True to keep existing selections
22528 selectLastRow : function(keepExisting){
22529 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22533 * Selects the row immediately following the last selected row.
22534 * @param {Boolean} keepExisting (optional) True to keep existing selections
22536 selectNext : function(keepExisting){
22537 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22538 this.selectRow(this.last+1, keepExisting);
22539 this.grid.getView().focusRow(this.last);
22544 * Selects the row that precedes the last selected row.
22545 * @param {Boolean} keepExisting (optional) True to keep existing selections
22547 selectPrevious : function(keepExisting){
22549 this.selectRow(this.last-1, keepExisting);
22550 this.grid.getView().focusRow(this.last);
22555 * Returns the selected records
22556 * @return {Array} Array of selected records
22558 getSelections : function(){
22559 return [].concat(this.selections.items);
22563 * Returns the first selected record.
22566 getSelected : function(){
22567 return this.selections.itemAt(0);
22572 * Clears all selections.
22574 clearSelections : function(fast){
22579 var ds = this.grid.dataSource;
22580 var s = this.selections;
22581 s.each(function(r){
22582 this.deselectRow(ds.indexOfId(r.id));
22586 this.selections.clear();
22593 * Selects all rows.
22595 selectAll : function(){
22599 this.selections.clear();
22600 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22601 this.selectRow(i, true);
22606 * Returns True if there is a selection.
22607 * @return {Boolean}
22609 hasSelection : function(){
22610 return this.selections.length > 0;
22614 * Returns True if the specified row is selected.
22615 * @param {Number/Record} record The record or index of the record to check
22616 * @return {Boolean}
22618 isSelected : function(index){
22619 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22620 return (r && this.selections.key(r.id) ? true : false);
22624 * Returns True if the specified record id is selected.
22625 * @param {String} id The id of record to check
22626 * @return {Boolean}
22628 isIdSelected : function(id){
22629 return (this.selections.key(id) ? true : false);
22633 handleMouseDown : function(e, t){
22634 var view = this.grid.getView(), rowIndex;
22635 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22638 if(e.shiftKey && this.last !== false){
22639 var last = this.last;
22640 this.selectRange(last, rowIndex, e.ctrlKey);
22641 this.last = last; // reset the last
22642 view.focusRow(rowIndex);
22644 var isSelected = this.isSelected(rowIndex);
22645 if(e.button !== 0 && isSelected){
22646 view.focusRow(rowIndex);
22647 }else if(e.ctrlKey && isSelected){
22648 this.deselectRow(rowIndex);
22649 }else if(!isSelected){
22650 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22651 view.focusRow(rowIndex);
22654 this.fireEvent("afterselectionchange", this);
22657 handleDragableRowClick : function(grid, rowIndex, e)
22659 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22660 this.selectRow(rowIndex, false);
22661 grid.view.focusRow(rowIndex);
22662 this.fireEvent("afterselectionchange", this);
22667 * Selects multiple rows.
22668 * @param {Array} rows Array of the indexes of the row to select
22669 * @param {Boolean} keepExisting (optional) True to keep existing selections
22671 selectRows : function(rows, keepExisting){
22673 this.clearSelections();
22675 for(var i = 0, len = rows.length; i < len; i++){
22676 this.selectRow(rows[i], true);
22681 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22682 * @param {Number} startRow The index of the first row in the range
22683 * @param {Number} endRow The index of the last row in the range
22684 * @param {Boolean} keepExisting (optional) True to retain existing selections
22686 selectRange : function(startRow, endRow, keepExisting){
22691 this.clearSelections();
22693 if(startRow <= endRow){
22694 for(var i = startRow; i <= endRow; i++){
22695 this.selectRow(i, true);
22698 for(var i = startRow; i >= endRow; i--){
22699 this.selectRow(i, true);
22705 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22706 * @param {Number} startRow The index of the first row in the range
22707 * @param {Number} endRow The index of the last row in the range
22709 deselectRange : function(startRow, endRow, preventViewNotify){
22713 for(var i = startRow; i <= endRow; i++){
22714 this.deselectRow(i, preventViewNotify);
22720 * @param {Number} row The index of the row to select
22721 * @param {Boolean} keepExisting (optional) True to keep existing selections
22723 selectRow : function(index, keepExisting, preventViewNotify){
22724 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22727 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22728 if(!keepExisting || this.singleSelect){
22729 this.clearSelections();
22731 var r = this.grid.dataSource.getAt(index);
22732 this.selections.add(r);
22733 this.last = this.lastActive = index;
22734 if(!preventViewNotify){
22735 this.grid.getView().onRowSelect(index);
22737 this.fireEvent("rowselect", this, index, r);
22738 this.fireEvent("selectionchange", this);
22744 * @param {Number} row The index of the row to deselect
22746 deselectRow : function(index, preventViewNotify){
22750 if(this.last == index){
22753 if(this.lastActive == index){
22754 this.lastActive = false;
22756 var r = this.grid.dataSource.getAt(index);
22757 this.selections.remove(r);
22758 if(!preventViewNotify){
22759 this.grid.getView().onRowDeselect(index);
22761 this.fireEvent("rowdeselect", this, index);
22762 this.fireEvent("selectionchange", this);
22766 restoreLast : function(){
22768 this.last = this._last;
22773 acceptsNav : function(row, col, cm){
22774 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22778 onEditorKey : function(field, e){
22779 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22784 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22786 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22788 }else if(k == e.ENTER && !e.ctrlKey){
22792 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22794 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22796 }else if(k == e.ESC){
22800 g.startEditing(newCell[0], newCell[1]);
22805 * Ext JS Library 1.1.1
22806 * Copyright(c) 2006-2007, Ext JS, LLC.
22808 * Originally Released Under LGPL - original licence link has changed is not relivant.
22811 * <script type="text/javascript">
22815 * @class Roo.bootstrap.PagingToolbar
22816 * @extends Roo.bootstrap.NavSimplebar
22817 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22819 * Create a new PagingToolbar
22820 * @param {Object} config The config object
22821 * @param {Roo.data.Store} store
22823 Roo.bootstrap.PagingToolbar = function(config)
22825 // old args format still supported... - xtype is prefered..
22826 // created from xtype...
22828 this.ds = config.dataSource;
22830 if (config.store && !this.ds) {
22831 this.store= Roo.factory(config.store, Roo.data);
22832 this.ds = this.store;
22833 this.ds.xmodule = this.xmodule || false;
22836 this.toolbarItems = [];
22837 if (config.items) {
22838 this.toolbarItems = config.items;
22841 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22846 this.bind(this.ds);
22849 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22853 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22855 * @cfg {Roo.data.Store} dataSource
22856 * The underlying data store providing the paged data
22859 * @cfg {String/HTMLElement/Element} container
22860 * container The id or element that will contain the toolbar
22863 * @cfg {Boolean} displayInfo
22864 * True to display the displayMsg (defaults to false)
22867 * @cfg {Number} pageSize
22868 * The number of records to display per page (defaults to 20)
22872 * @cfg {String} displayMsg
22873 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22875 displayMsg : 'Displaying {0} - {1} of {2}',
22877 * @cfg {String} emptyMsg
22878 * The message to display when no records are found (defaults to "No data to display")
22880 emptyMsg : 'No data to display',
22882 * Customizable piece of the default paging text (defaults to "Page")
22885 beforePageText : "Page",
22887 * Customizable piece of the default paging text (defaults to "of %0")
22890 afterPageText : "of {0}",
22892 * Customizable piece of the default paging text (defaults to "First Page")
22895 firstText : "First Page",
22897 * Customizable piece of the default paging text (defaults to "Previous Page")
22900 prevText : "Previous Page",
22902 * Customizable piece of the default paging text (defaults to "Next Page")
22905 nextText : "Next Page",
22907 * Customizable piece of the default paging text (defaults to "Last Page")
22910 lastText : "Last Page",
22912 * Customizable piece of the default paging text (defaults to "Refresh")
22915 refreshText : "Refresh",
22919 onRender : function(ct, position)
22921 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22922 this.navgroup.parentId = this.id;
22923 this.navgroup.onRender(this.el, null);
22924 // add the buttons to the navgroup
22926 if(this.displayInfo){
22927 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22928 this.displayEl = this.el.select('.x-paging-info', true).first();
22929 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22930 // this.displayEl = navel.el.select('span',true).first();
22936 Roo.each(_this.buttons, function(e){ // this might need to use render????
22937 Roo.factory(e).onRender(_this.el, null);
22941 Roo.each(_this.toolbarItems, function(e) {
22942 _this.navgroup.addItem(e);
22946 this.first = this.navgroup.addItem({
22947 tooltip: this.firstText,
22949 icon : 'fa fa-backward',
22951 preventDefault: true,
22952 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22955 this.prev = this.navgroup.addItem({
22956 tooltip: this.prevText,
22958 icon : 'fa fa-step-backward',
22960 preventDefault: true,
22961 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22963 //this.addSeparator();
22966 var field = this.navgroup.addItem( {
22968 cls : 'x-paging-position',
22970 html : this.beforePageText +
22971 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22972 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22975 this.field = field.el.select('input', true).first();
22976 this.field.on("keydown", this.onPagingKeydown, this);
22977 this.field.on("focus", function(){this.dom.select();});
22980 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22981 //this.field.setHeight(18);
22982 //this.addSeparator();
22983 this.next = this.navgroup.addItem({
22984 tooltip: this.nextText,
22986 html : ' <i class="fa fa-step-forward">',
22988 preventDefault: true,
22989 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22991 this.last = this.navgroup.addItem({
22992 tooltip: this.lastText,
22993 icon : 'fa fa-forward',
22996 preventDefault: true,
22997 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22999 //this.addSeparator();
23000 this.loading = this.navgroup.addItem({
23001 tooltip: this.refreshText,
23002 icon: 'fa fa-refresh',
23003 preventDefault: true,
23004 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23010 updateInfo : function(){
23011 if(this.displayEl){
23012 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23013 var msg = count == 0 ?
23017 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23019 this.displayEl.update(msg);
23024 onLoad : function(ds, r, o){
23025 this.cursor = o.params ? o.params.start : 0;
23026 var d = this.getPageData(),
23030 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23031 this.field.dom.value = ap;
23032 this.first.setDisabled(ap == 1);
23033 this.prev.setDisabled(ap == 1);
23034 this.next.setDisabled(ap == ps);
23035 this.last.setDisabled(ap == ps);
23036 this.loading.enable();
23041 getPageData : function(){
23042 var total = this.ds.getTotalCount();
23045 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23046 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23051 onLoadError : function(){
23052 this.loading.enable();
23056 onPagingKeydown : function(e){
23057 var k = e.getKey();
23058 var d = this.getPageData();
23060 var v = this.field.dom.value, pageNum;
23061 if(!v || isNaN(pageNum = parseInt(v, 10))){
23062 this.field.dom.value = d.activePage;
23065 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23066 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23069 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))
23071 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23072 this.field.dom.value = pageNum;
23073 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23076 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23078 var v = this.field.dom.value, pageNum;
23079 var increment = (e.shiftKey) ? 10 : 1;
23080 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23083 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23084 this.field.dom.value = d.activePage;
23087 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23089 this.field.dom.value = parseInt(v, 10) + increment;
23090 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23091 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23098 beforeLoad : function(){
23100 this.loading.disable();
23105 onClick : function(which){
23114 ds.load({params:{start: 0, limit: this.pageSize}});
23117 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23120 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23123 var total = ds.getTotalCount();
23124 var extra = total % this.pageSize;
23125 var lastStart = extra ? (total - extra) : total-this.pageSize;
23126 ds.load({params:{start: lastStart, limit: this.pageSize}});
23129 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23135 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23136 * @param {Roo.data.Store} store The data store to unbind
23138 unbind : function(ds){
23139 ds.un("beforeload", this.beforeLoad, this);
23140 ds.un("load", this.onLoad, this);
23141 ds.un("loadexception", this.onLoadError, this);
23142 ds.un("remove", this.updateInfo, this);
23143 ds.un("add", this.updateInfo, this);
23144 this.ds = undefined;
23148 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23149 * @param {Roo.data.Store} store The data store to bind
23151 bind : function(ds){
23152 ds.on("beforeload", this.beforeLoad, this);
23153 ds.on("load", this.onLoad, this);
23154 ds.on("loadexception", this.onLoadError, this);
23155 ds.on("remove", this.updateInfo, this);
23156 ds.on("add", this.updateInfo, this);
23167 * @class Roo.bootstrap.MessageBar
23168 * @extends Roo.bootstrap.Component
23169 * Bootstrap MessageBar class
23170 * @cfg {String} html contents of the MessageBar
23171 * @cfg {String} weight (info | success | warning | danger) default info
23172 * @cfg {String} beforeClass insert the bar before the given class
23173 * @cfg {Boolean} closable (true | false) default false
23174 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23177 * Create a new Element
23178 * @param {Object} config The config object
23181 Roo.bootstrap.MessageBar = function(config){
23182 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23185 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23191 beforeClass: 'bootstrap-sticky-wrap',
23193 getAutoCreate : function(){
23197 cls: 'alert alert-dismissable alert-' + this.weight,
23202 html: this.html || ''
23208 cfg.cls += ' alert-messages-fixed';
23222 onRender : function(ct, position)
23224 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23227 var cfg = Roo.apply({}, this.getAutoCreate());
23231 cfg.cls += ' ' + this.cls;
23234 cfg.style = this.style;
23236 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23238 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23241 this.el.select('>button.close').on('click', this.hide, this);
23247 if (!this.rendered) {
23253 this.fireEvent('show', this);
23259 if (!this.rendered) {
23265 this.fireEvent('hide', this);
23268 update : function()
23270 // var e = this.el.dom.firstChild;
23272 // if(this.closable){
23273 // e = e.nextSibling;
23276 // e.data = this.html || '';
23278 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23294 * @class Roo.bootstrap.Graph
23295 * @extends Roo.bootstrap.Component
23296 * Bootstrap Graph class
23300 @cfg {String} graphtype bar | vbar | pie
23301 @cfg {number} g_x coodinator | centre x (pie)
23302 @cfg {number} g_y coodinator | centre y (pie)
23303 @cfg {number} g_r radius (pie)
23304 @cfg {number} g_height height of the chart (respected by all elements in the set)
23305 @cfg {number} g_width width of the chart (respected by all elements in the set)
23306 @cfg {Object} title The title of the chart
23309 -opts (object) options for the chart
23311 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23312 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23314 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.
23315 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23317 o stretch (boolean)
23319 -opts (object) options for the pie
23322 o startAngle (number)
23323 o endAngle (number)
23327 * Create a new Input
23328 * @param {Object} config The config object
23331 Roo.bootstrap.Graph = function(config){
23332 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23338 * The img click event for the img.
23339 * @param {Roo.EventObject} e
23345 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23356 //g_colors: this.colors,
23363 getAutoCreate : function(){
23374 onRender : function(ct,position){
23377 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23379 if (typeof(Raphael) == 'undefined') {
23380 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23384 this.raphael = Raphael(this.el.dom);
23386 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23387 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23388 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23389 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23391 r.text(160, 10, "Single Series Chart").attr(txtattr);
23392 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23393 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23394 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23396 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23397 r.barchart(330, 10, 300, 220, data1);
23398 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23399 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23402 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23403 // r.barchart(30, 30, 560, 250, xdata, {
23404 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23405 // axis : "0 0 1 1",
23406 // axisxlabels : xdata
23407 // //yvalues : cols,
23410 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23412 // this.load(null,xdata,{
23413 // axis : "0 0 1 1",
23414 // axisxlabels : xdata
23419 load : function(graphtype,xdata,opts)
23421 this.raphael.clear();
23423 graphtype = this.graphtype;
23428 var r = this.raphael,
23429 fin = function () {
23430 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23432 fout = function () {
23433 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23435 pfin = function() {
23436 this.sector.stop();
23437 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23440 this.label[0].stop();
23441 this.label[0].attr({ r: 7.5 });
23442 this.label[1].attr({ "font-weight": 800 });
23445 pfout = function() {
23446 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23449 this.label[0].animate({ r: 5 }, 500, "bounce");
23450 this.label[1].attr({ "font-weight": 400 });
23456 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23459 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23462 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23463 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23465 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23472 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23477 setTitle: function(o)
23482 initEvents: function() {
23485 this.el.on('click', this.onClick, this);
23489 onClick : function(e)
23491 Roo.log('img onclick');
23492 this.fireEvent('click', this, e);
23504 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23507 * @class Roo.bootstrap.dash.NumberBox
23508 * @extends Roo.bootstrap.Component
23509 * Bootstrap NumberBox class
23510 * @cfg {String} headline Box headline
23511 * @cfg {String} content Box content
23512 * @cfg {String} icon Box icon
23513 * @cfg {String} footer Footer text
23514 * @cfg {String} fhref Footer href
23517 * Create a new NumberBox
23518 * @param {Object} config The config object
23522 Roo.bootstrap.dash.NumberBox = function(config){
23523 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23527 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23536 getAutoCreate : function(){
23540 cls : 'small-box ',
23548 cls : 'roo-headline',
23549 html : this.headline
23553 cls : 'roo-content',
23554 html : this.content
23568 cls : 'ion ' + this.icon
23577 cls : 'small-box-footer',
23578 href : this.fhref || '#',
23582 cfg.cn.push(footer);
23589 onRender : function(ct,position){
23590 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23597 setHeadline: function (value)
23599 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23602 setFooter: function (value, href)
23604 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23607 this.el.select('a.small-box-footer',true).first().attr('href', href);
23612 setContent: function (value)
23614 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23617 initEvents: function()
23631 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23634 * @class Roo.bootstrap.dash.TabBox
23635 * @extends Roo.bootstrap.Component
23636 * Bootstrap TabBox class
23637 * @cfg {String} title Title of the TabBox
23638 * @cfg {String} icon Icon of the TabBox
23639 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23640 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23643 * Create a new TabBox
23644 * @param {Object} config The config object
23648 Roo.bootstrap.dash.TabBox = function(config){
23649 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23654 * When a pane is added
23655 * @param {Roo.bootstrap.dash.TabPane} pane
23659 * @event activatepane
23660 * When a pane is activated
23661 * @param {Roo.bootstrap.dash.TabPane} pane
23663 "activatepane" : true
23671 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23676 tabScrollable : false,
23678 getChildContainer : function()
23680 return this.el.select('.tab-content', true).first();
23683 getAutoCreate : function(){
23687 cls: 'pull-left header',
23695 cls: 'fa ' + this.icon
23701 cls: 'nav nav-tabs pull-right',
23707 if(this.tabScrollable){
23714 cls: 'nav nav-tabs pull-right',
23725 cls: 'nav-tabs-custom',
23730 cls: 'tab-content no-padding',
23738 initEvents : function()
23740 //Roo.log('add add pane handler');
23741 this.on('addpane', this.onAddPane, this);
23744 * Updates the box title
23745 * @param {String} html to set the title to.
23747 setTitle : function(value)
23749 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23751 onAddPane : function(pane)
23753 this.panes.push(pane);
23754 //Roo.log('addpane');
23756 // tabs are rendere left to right..
23757 if(!this.showtabs){
23761 var ctr = this.el.select('.nav-tabs', true).first();
23764 var existing = ctr.select('.nav-tab',true);
23765 var qty = existing.getCount();;
23768 var tab = ctr.createChild({
23770 cls : 'nav-tab' + (qty ? '' : ' active'),
23778 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23781 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23783 pane.el.addClass('active');
23788 onTabClick : function(ev,un,ob,pane)
23790 //Roo.log('tab - prev default');
23791 ev.preventDefault();
23794 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23795 pane.tab.addClass('active');
23796 //Roo.log(pane.title);
23797 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23798 // technically we should have a deactivate event.. but maybe add later.
23799 // and it should not de-activate the selected tab...
23800 this.fireEvent('activatepane', pane);
23801 pane.el.addClass('active');
23802 pane.fireEvent('activate');
23807 getActivePane : function()
23810 Roo.each(this.panes, function(p) {
23811 if(p.el.hasClass('active')){
23832 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23834 * @class Roo.bootstrap.TabPane
23835 * @extends Roo.bootstrap.Component
23836 * Bootstrap TabPane class
23837 * @cfg {Boolean} active (false | true) Default false
23838 * @cfg {String} title title of panel
23842 * Create a new TabPane
23843 * @param {Object} config The config object
23846 Roo.bootstrap.dash.TabPane = function(config){
23847 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23853 * When a pane is activated
23854 * @param {Roo.bootstrap.dash.TabPane} pane
23861 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23866 // the tabBox that this is attached to.
23869 getAutoCreate : function()
23877 cfg.cls += ' active';
23882 initEvents : function()
23884 //Roo.log('trigger add pane handler');
23885 this.parent().fireEvent('addpane', this)
23889 * Updates the tab title
23890 * @param {String} html to set the title to.
23892 setTitle: function(str)
23898 this.tab.select('a', true).first().dom.innerHTML = str;
23915 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23918 * @class Roo.bootstrap.menu.Menu
23919 * @extends Roo.bootstrap.Component
23920 * Bootstrap Menu class - container for Menu
23921 * @cfg {String} html Text of the menu
23922 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23923 * @cfg {String} icon Font awesome icon
23924 * @cfg {String} pos Menu align to (top | bottom) default bottom
23928 * Create a new Menu
23929 * @param {Object} config The config object
23933 Roo.bootstrap.menu.Menu = function(config){
23934 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23938 * @event beforeshow
23939 * Fires before this menu is displayed
23940 * @param {Roo.bootstrap.menu.Menu} this
23944 * @event beforehide
23945 * Fires before this menu is hidden
23946 * @param {Roo.bootstrap.menu.Menu} this
23951 * Fires after this menu is displayed
23952 * @param {Roo.bootstrap.menu.Menu} this
23957 * Fires after this menu is hidden
23958 * @param {Roo.bootstrap.menu.Menu} this
23963 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23964 * @param {Roo.bootstrap.menu.Menu} this
23965 * @param {Roo.EventObject} e
23972 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23976 weight : 'default',
23981 getChildContainer : function() {
23982 if(this.isSubMenu){
23986 return this.el.select('ul.dropdown-menu', true).first();
23989 getAutoCreate : function()
23994 cls : 'roo-menu-text',
24002 cls : 'fa ' + this.icon
24013 cls : 'dropdown-button btn btn-' + this.weight,
24018 cls : 'dropdown-toggle btn btn-' + this.weight,
24028 cls : 'dropdown-menu'
24034 if(this.pos == 'top'){
24035 cfg.cls += ' dropup';
24038 if(this.isSubMenu){
24041 cls : 'dropdown-menu'
24048 onRender : function(ct, position)
24050 this.isSubMenu = ct.hasClass('dropdown-submenu');
24052 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24055 initEvents : function()
24057 if(this.isSubMenu){
24061 this.hidden = true;
24063 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24064 this.triggerEl.on('click', this.onTriggerPress, this);
24066 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24067 this.buttonEl.on('click', this.onClick, this);
24073 if(this.isSubMenu){
24077 return this.el.select('ul.dropdown-menu', true).first();
24080 onClick : function(e)
24082 this.fireEvent("click", this, e);
24085 onTriggerPress : function(e)
24087 if (this.isVisible()) {
24094 isVisible : function(){
24095 return !this.hidden;
24100 this.fireEvent("beforeshow", this);
24102 this.hidden = false;
24103 this.el.addClass('open');
24105 Roo.get(document).on("mouseup", this.onMouseUp, this);
24107 this.fireEvent("show", this);
24114 this.fireEvent("beforehide", this);
24116 this.hidden = true;
24117 this.el.removeClass('open');
24119 Roo.get(document).un("mouseup", this.onMouseUp);
24121 this.fireEvent("hide", this);
24124 onMouseUp : function()
24138 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24141 * @class Roo.bootstrap.menu.Item
24142 * @extends Roo.bootstrap.Component
24143 * Bootstrap MenuItem class
24144 * @cfg {Boolean} submenu (true | false) default false
24145 * @cfg {String} html text of the item
24146 * @cfg {String} href the link
24147 * @cfg {Boolean} disable (true | false) default false
24148 * @cfg {Boolean} preventDefault (true | false) default true
24149 * @cfg {String} icon Font awesome icon
24150 * @cfg {String} pos Submenu align to (left | right) default right
24154 * Create a new Item
24155 * @param {Object} config The config object
24159 Roo.bootstrap.menu.Item = function(config){
24160 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24164 * Fires when the mouse is hovering over this menu
24165 * @param {Roo.bootstrap.menu.Item} this
24166 * @param {Roo.EventObject} e
24171 * Fires when the mouse exits this menu
24172 * @param {Roo.bootstrap.menu.Item} this
24173 * @param {Roo.EventObject} e
24179 * The raw click event for the entire grid.
24180 * @param {Roo.EventObject} e
24186 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24191 preventDefault: true,
24196 getAutoCreate : function()
24201 cls : 'roo-menu-item-text',
24209 cls : 'fa ' + this.icon
24218 href : this.href || '#',
24225 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24229 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24231 if(this.pos == 'left'){
24232 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24239 initEvents : function()
24241 this.el.on('mouseover', this.onMouseOver, this);
24242 this.el.on('mouseout', this.onMouseOut, this);
24244 this.el.select('a', true).first().on('click', this.onClick, this);
24248 onClick : function(e)
24250 if(this.preventDefault){
24251 e.preventDefault();
24254 this.fireEvent("click", this, e);
24257 onMouseOver : function(e)
24259 if(this.submenu && this.pos == 'left'){
24260 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24263 this.fireEvent("mouseover", this, e);
24266 onMouseOut : function(e)
24268 this.fireEvent("mouseout", this, e);
24280 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24283 * @class Roo.bootstrap.menu.Separator
24284 * @extends Roo.bootstrap.Component
24285 * Bootstrap Separator class
24288 * Create a new Separator
24289 * @param {Object} config The config object
24293 Roo.bootstrap.menu.Separator = function(config){
24294 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24297 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24299 getAutoCreate : function(){
24320 * @class Roo.bootstrap.Tooltip
24321 * Bootstrap Tooltip class
24322 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24323 * to determine which dom element triggers the tooltip.
24325 * It needs to add support for additional attributes like tooltip-position
24328 * Create a new Toolti
24329 * @param {Object} config The config object
24332 Roo.bootstrap.Tooltip = function(config){
24333 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24336 Roo.apply(Roo.bootstrap.Tooltip, {
24338 * @function init initialize tooltip monitoring.
24342 currentTip : false,
24343 currentRegion : false,
24349 Roo.get(document).on('mouseover', this.enter ,this);
24350 Roo.get(document).on('mouseout', this.leave, this);
24353 this.currentTip = new Roo.bootstrap.Tooltip();
24356 enter : function(ev)
24358 var dom = ev.getTarget();
24360 //Roo.log(['enter',dom]);
24361 var el = Roo.fly(dom);
24362 if (this.currentEl) {
24364 //Roo.log(this.currentEl);
24365 //Roo.log(this.currentEl.contains(dom));
24366 if (this.currentEl == el) {
24369 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24375 if (this.currentTip.el) {
24376 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24380 if(!el || el.dom == document){
24386 // you can not look for children, as if el is the body.. then everythign is the child..
24387 if (!el.attr('tooltip')) { //
24388 if (!el.select("[tooltip]").elements.length) {
24391 // is the mouse over this child...?
24392 bindEl = el.select("[tooltip]").first();
24393 var xy = ev.getXY();
24394 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24395 //Roo.log("not in region.");
24398 //Roo.log("child element over..");
24401 this.currentEl = bindEl;
24402 this.currentTip.bind(bindEl);
24403 this.currentRegion = Roo.lib.Region.getRegion(dom);
24404 this.currentTip.enter();
24407 leave : function(ev)
24409 var dom = ev.getTarget();
24410 //Roo.log(['leave',dom]);
24411 if (!this.currentEl) {
24416 if (dom != this.currentEl.dom) {
24419 var xy = ev.getXY();
24420 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24423 // only activate leave if mouse cursor is outside... bounding box..
24428 if (this.currentTip) {
24429 this.currentTip.leave();
24431 //Roo.log('clear currentEl');
24432 this.currentEl = false;
24437 'left' : ['r-l', [-2,0], 'right'],
24438 'right' : ['l-r', [2,0], 'left'],
24439 'bottom' : ['t-b', [0,2], 'top'],
24440 'top' : [ 'b-t', [0,-2], 'bottom']
24446 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24451 delay : null, // can be { show : 300 , hide: 500}
24455 hoverState : null, //???
24457 placement : 'bottom',
24459 getAutoCreate : function(){
24466 cls : 'tooltip-arrow'
24469 cls : 'tooltip-inner'
24476 bind : function(el)
24482 enter : function () {
24484 if (this.timeout != null) {
24485 clearTimeout(this.timeout);
24488 this.hoverState = 'in';
24489 //Roo.log("enter - show");
24490 if (!this.delay || !this.delay.show) {
24495 this.timeout = setTimeout(function () {
24496 if (_t.hoverState == 'in') {
24499 }, this.delay.show);
24503 clearTimeout(this.timeout);
24505 this.hoverState = 'out';
24506 if (!this.delay || !this.delay.hide) {
24512 this.timeout = setTimeout(function () {
24513 //Roo.log("leave - timeout");
24515 if (_t.hoverState == 'out') {
24517 Roo.bootstrap.Tooltip.currentEl = false;
24525 this.render(document.body);
24528 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24530 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24532 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24534 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24536 var placement = typeof this.placement == 'function' ?
24537 this.placement.call(this, this.el, on_el) :
24540 var autoToken = /\s?auto?\s?/i;
24541 var autoPlace = autoToken.test(placement);
24543 placement = placement.replace(autoToken, '') || 'top';
24547 //this.el.setXY([0,0]);
24549 //this.el.dom.style.display='block';
24551 //this.el.appendTo(on_el);
24553 var p = this.getPosition();
24554 var box = this.el.getBox();
24560 var align = Roo.bootstrap.Tooltip.alignment[placement];
24562 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24564 if(placement == 'top' || placement == 'bottom'){
24566 placement = 'right';
24569 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24570 placement = 'left';
24573 var scroll = Roo.select('body', true).first().getScroll();
24575 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24581 align = Roo.bootstrap.Tooltip.alignment[placement];
24583 this.el.alignTo(this.bindEl, align[0],align[1]);
24584 //var arrow = this.el.select('.arrow',true).first();
24585 //arrow.set(align[2],
24587 this.el.addClass(placement);
24589 this.el.addClass('in fade');
24591 this.hoverState = null;
24593 if (this.el.hasClass('fade')) {
24604 //this.el.setXY([0,0]);
24605 this.el.removeClass('in');
24621 * @class Roo.bootstrap.LocationPicker
24622 * @extends Roo.bootstrap.Component
24623 * Bootstrap LocationPicker class
24624 * @cfg {Number} latitude Position when init default 0
24625 * @cfg {Number} longitude Position when init default 0
24626 * @cfg {Number} zoom default 15
24627 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24628 * @cfg {Boolean} mapTypeControl default false
24629 * @cfg {Boolean} disableDoubleClickZoom default false
24630 * @cfg {Boolean} scrollwheel default true
24631 * @cfg {Boolean} streetViewControl default false
24632 * @cfg {Number} radius default 0
24633 * @cfg {String} locationName
24634 * @cfg {Boolean} draggable default true
24635 * @cfg {Boolean} enableAutocomplete default false
24636 * @cfg {Boolean} enableReverseGeocode default true
24637 * @cfg {String} markerTitle
24640 * Create a new LocationPicker
24641 * @param {Object} config The config object
24645 Roo.bootstrap.LocationPicker = function(config){
24647 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24652 * Fires when the picker initialized.
24653 * @param {Roo.bootstrap.LocationPicker} this
24654 * @param {Google Location} location
24658 * @event positionchanged
24659 * Fires when the picker position changed.
24660 * @param {Roo.bootstrap.LocationPicker} this
24661 * @param {Google Location} location
24663 positionchanged : true,
24666 * Fires when the map resize.
24667 * @param {Roo.bootstrap.LocationPicker} this
24672 * Fires when the map show.
24673 * @param {Roo.bootstrap.LocationPicker} this
24678 * Fires when the map hide.
24679 * @param {Roo.bootstrap.LocationPicker} this
24684 * Fires when click the map.
24685 * @param {Roo.bootstrap.LocationPicker} this
24686 * @param {Map event} e
24690 * @event mapRightClick
24691 * Fires when right click the map.
24692 * @param {Roo.bootstrap.LocationPicker} this
24693 * @param {Map event} e
24695 mapRightClick : true,
24697 * @event markerClick
24698 * Fires when click the marker.
24699 * @param {Roo.bootstrap.LocationPicker} this
24700 * @param {Map event} e
24702 markerClick : true,
24704 * @event markerRightClick
24705 * Fires when right click the marker.
24706 * @param {Roo.bootstrap.LocationPicker} this
24707 * @param {Map event} e
24709 markerRightClick : true,
24711 * @event OverlayViewDraw
24712 * Fires when OverlayView Draw
24713 * @param {Roo.bootstrap.LocationPicker} this
24715 OverlayViewDraw : true,
24717 * @event OverlayViewOnAdd
24718 * Fires when OverlayView Draw
24719 * @param {Roo.bootstrap.LocationPicker} this
24721 OverlayViewOnAdd : true,
24723 * @event OverlayViewOnRemove
24724 * Fires when OverlayView Draw
24725 * @param {Roo.bootstrap.LocationPicker} this
24727 OverlayViewOnRemove : true,
24729 * @event OverlayViewShow
24730 * Fires when OverlayView Draw
24731 * @param {Roo.bootstrap.LocationPicker} this
24732 * @param {Pixel} cpx
24734 OverlayViewShow : true,
24736 * @event OverlayViewHide
24737 * Fires when OverlayView Draw
24738 * @param {Roo.bootstrap.LocationPicker} this
24740 OverlayViewHide : true,
24742 * @event loadexception
24743 * Fires when load google lib failed.
24744 * @param {Roo.bootstrap.LocationPicker} this
24746 loadexception : true
24751 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24753 gMapContext: false,
24759 mapTypeControl: false,
24760 disableDoubleClickZoom: false,
24762 streetViewControl: false,
24766 enableAutocomplete: false,
24767 enableReverseGeocode: true,
24770 getAutoCreate: function()
24775 cls: 'roo-location-picker'
24781 initEvents: function(ct, position)
24783 if(!this.el.getWidth() || this.isApplied()){
24787 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24792 initial: function()
24794 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24795 this.fireEvent('loadexception', this);
24799 if(!this.mapTypeId){
24800 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24803 this.gMapContext = this.GMapContext();
24805 this.initOverlayView();
24807 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24811 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24812 _this.setPosition(_this.gMapContext.marker.position);
24815 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24816 _this.fireEvent('mapClick', this, event);
24820 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24821 _this.fireEvent('mapRightClick', this, event);
24825 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24826 _this.fireEvent('markerClick', this, event);
24830 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24831 _this.fireEvent('markerRightClick', this, event);
24835 this.setPosition(this.gMapContext.location);
24837 this.fireEvent('initial', this, this.gMapContext.location);
24840 initOverlayView: function()
24844 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24848 _this.fireEvent('OverlayViewDraw', _this);
24853 _this.fireEvent('OverlayViewOnAdd', _this);
24856 onRemove: function()
24858 _this.fireEvent('OverlayViewOnRemove', _this);
24861 show: function(cpx)
24863 _this.fireEvent('OverlayViewShow', _this, cpx);
24868 _this.fireEvent('OverlayViewHide', _this);
24874 fromLatLngToContainerPixel: function(event)
24876 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24879 isApplied: function()
24881 return this.getGmapContext() == false ? false : true;
24884 getGmapContext: function()
24886 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24889 GMapContext: function()
24891 var position = new google.maps.LatLng(this.latitude, this.longitude);
24893 var _map = new google.maps.Map(this.el.dom, {
24896 mapTypeId: this.mapTypeId,
24897 mapTypeControl: this.mapTypeControl,
24898 disableDoubleClickZoom: this.disableDoubleClickZoom,
24899 scrollwheel: this.scrollwheel,
24900 streetViewControl: this.streetViewControl,
24901 locationName: this.locationName,
24902 draggable: this.draggable,
24903 enableAutocomplete: this.enableAutocomplete,
24904 enableReverseGeocode: this.enableReverseGeocode
24907 var _marker = new google.maps.Marker({
24908 position: position,
24910 title: this.markerTitle,
24911 draggable: this.draggable
24918 location: position,
24919 radius: this.radius,
24920 locationName: this.locationName,
24921 addressComponents: {
24922 formatted_address: null,
24923 addressLine1: null,
24924 addressLine2: null,
24926 streetNumber: null,
24930 stateOrProvince: null
24933 domContainer: this.el.dom,
24934 geodecoder: new google.maps.Geocoder()
24938 drawCircle: function(center, radius, options)
24940 if (this.gMapContext.circle != null) {
24941 this.gMapContext.circle.setMap(null);
24945 options = Roo.apply({}, options, {
24946 strokeColor: "#0000FF",
24947 strokeOpacity: .35,
24949 fillColor: "#0000FF",
24953 options.map = this.gMapContext.map;
24954 options.radius = radius;
24955 options.center = center;
24956 this.gMapContext.circle = new google.maps.Circle(options);
24957 return this.gMapContext.circle;
24963 setPosition: function(location)
24965 this.gMapContext.location = location;
24966 this.gMapContext.marker.setPosition(location);
24967 this.gMapContext.map.panTo(location);
24968 this.drawCircle(location, this.gMapContext.radius, {});
24972 if (this.gMapContext.settings.enableReverseGeocode) {
24973 this.gMapContext.geodecoder.geocode({
24974 latLng: this.gMapContext.location
24975 }, function(results, status) {
24977 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24978 _this.gMapContext.locationName = results[0].formatted_address;
24979 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24981 _this.fireEvent('positionchanged', this, location);
24988 this.fireEvent('positionchanged', this, location);
24993 google.maps.event.trigger(this.gMapContext.map, "resize");
24995 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24997 this.fireEvent('resize', this);
25000 setPositionByLatLng: function(latitude, longitude)
25002 this.setPosition(new google.maps.LatLng(latitude, longitude));
25005 getCurrentPosition: function()
25008 latitude: this.gMapContext.location.lat(),
25009 longitude: this.gMapContext.location.lng()
25013 getAddressName: function()
25015 return this.gMapContext.locationName;
25018 getAddressComponents: function()
25020 return this.gMapContext.addressComponents;
25023 address_component_from_google_geocode: function(address_components)
25027 for (var i = 0; i < address_components.length; i++) {
25028 var component = address_components[i];
25029 if (component.types.indexOf("postal_code") >= 0) {
25030 result.postalCode = component.short_name;
25031 } else if (component.types.indexOf("street_number") >= 0) {
25032 result.streetNumber = component.short_name;
25033 } else if (component.types.indexOf("route") >= 0) {
25034 result.streetName = component.short_name;
25035 } else if (component.types.indexOf("neighborhood") >= 0) {
25036 result.city = component.short_name;
25037 } else if (component.types.indexOf("locality") >= 0) {
25038 result.city = component.short_name;
25039 } else if (component.types.indexOf("sublocality") >= 0) {
25040 result.district = component.short_name;
25041 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25042 result.stateOrProvince = component.short_name;
25043 } else if (component.types.indexOf("country") >= 0) {
25044 result.country = component.short_name;
25048 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25049 result.addressLine2 = "";
25053 setZoomLevel: function(zoom)
25055 this.gMapContext.map.setZoom(zoom);
25068 this.fireEvent('show', this);
25079 this.fireEvent('hide', this);
25084 Roo.apply(Roo.bootstrap.LocationPicker, {
25086 OverlayView : function(map, options)
25088 options = options || {};
25102 * @class Roo.bootstrap.Alert
25103 * @extends Roo.bootstrap.Component
25104 * Bootstrap Alert class
25105 * @cfg {String} title The title of alert
25106 * @cfg {String} html The content of alert
25107 * @cfg {String} weight ( success | info | warning | danger )
25108 * @cfg {String} faicon font-awesomeicon
25111 * Create a new alert
25112 * @param {Object} config The config object
25116 Roo.bootstrap.Alert = function(config){
25117 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25121 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25128 getAutoCreate : function()
25137 cls : 'roo-alert-icon'
25142 cls : 'roo-alert-title',
25147 cls : 'roo-alert-text',
25154 cfg.cn[0].cls += ' fa ' + this.faicon;
25158 cfg.cls += ' alert-' + this.weight;
25164 initEvents: function()
25166 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25169 setTitle : function(str)
25171 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25174 setText : function(str)
25176 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25179 setWeight : function(weight)
25182 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25185 this.weight = weight;
25187 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25190 setIcon : function(icon)
25193 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25196 this.faicon = icon;
25198 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25219 * @class Roo.bootstrap.UploadCropbox
25220 * @extends Roo.bootstrap.Component
25221 * Bootstrap UploadCropbox class
25222 * @cfg {String} emptyText show when image has been loaded
25223 * @cfg {String} rotateNotify show when image too small to rotate
25224 * @cfg {Number} errorTimeout default 3000
25225 * @cfg {Number} minWidth default 300
25226 * @cfg {Number} minHeight default 300
25227 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25228 * @cfg {Boolean} isDocument (true|false) default false
25229 * @cfg {String} url action url
25230 * @cfg {String} paramName default 'imageUpload'
25231 * @cfg {String} method default POST
25232 * @cfg {Boolean} loadMask (true|false) default true
25233 * @cfg {Boolean} loadingText default 'Loading...'
25236 * Create a new UploadCropbox
25237 * @param {Object} config The config object
25240 Roo.bootstrap.UploadCropbox = function(config){
25241 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25245 * @event beforeselectfile
25246 * Fire before select file
25247 * @param {Roo.bootstrap.UploadCropbox} this
25249 "beforeselectfile" : true,
25252 * Fire after initEvent
25253 * @param {Roo.bootstrap.UploadCropbox} this
25258 * Fire after initEvent
25259 * @param {Roo.bootstrap.UploadCropbox} this
25260 * @param {String} data
25265 * Fire when preparing the file data
25266 * @param {Roo.bootstrap.UploadCropbox} this
25267 * @param {Object} file
25272 * Fire when get exception
25273 * @param {Roo.bootstrap.UploadCropbox} this
25274 * @param {XMLHttpRequest} xhr
25276 "exception" : true,
25278 * @event beforeloadcanvas
25279 * Fire before load the canvas
25280 * @param {Roo.bootstrap.UploadCropbox} this
25281 * @param {String} src
25283 "beforeloadcanvas" : true,
25286 * Fire when trash image
25287 * @param {Roo.bootstrap.UploadCropbox} this
25292 * Fire when download the image
25293 * @param {Roo.bootstrap.UploadCropbox} this
25297 * @event footerbuttonclick
25298 * Fire when footerbuttonclick
25299 * @param {Roo.bootstrap.UploadCropbox} this
25300 * @param {String} type
25302 "footerbuttonclick" : true,
25306 * @param {Roo.bootstrap.UploadCropbox} this
25311 * Fire when rotate the image
25312 * @param {Roo.bootstrap.UploadCropbox} this
25313 * @param {String} pos
25318 * Fire when inspect the file
25319 * @param {Roo.bootstrap.UploadCropbox} this
25320 * @param {Object} file
25325 * Fire when xhr upload the file
25326 * @param {Roo.bootstrap.UploadCropbox} this
25327 * @param {Object} data
25332 * Fire when arrange the file data
25333 * @param {Roo.bootstrap.UploadCropbox} this
25334 * @param {Object} formData
25339 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25342 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25344 emptyText : 'Click to upload image',
25345 rotateNotify : 'Image is too small to rotate',
25346 errorTimeout : 3000,
25360 cropType : 'image/jpeg',
25362 canvasLoaded : false,
25363 isDocument : false,
25365 paramName : 'imageUpload',
25367 loadingText : 'Loading...',
25370 getAutoCreate : function()
25374 cls : 'roo-upload-cropbox',
25378 cls : 'roo-upload-cropbox-selector',
25383 cls : 'roo-upload-cropbox-body',
25384 style : 'cursor:pointer',
25388 cls : 'roo-upload-cropbox-preview'
25392 cls : 'roo-upload-cropbox-thumb'
25396 cls : 'roo-upload-cropbox-empty-notify',
25397 html : this.emptyText
25401 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25402 html : this.rotateNotify
25408 cls : 'roo-upload-cropbox-footer',
25411 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25421 onRender : function(ct, position)
25423 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25425 if (this.buttons.length) {
25427 Roo.each(this.buttons, function(bb) {
25429 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25431 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25437 this.maskEl = this.el;
25441 initEvents : function()
25443 this.urlAPI = (window.createObjectURL && window) ||
25444 (window.URL && URL.revokeObjectURL && URL) ||
25445 (window.webkitURL && webkitURL);
25447 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25448 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25450 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25451 this.selectorEl.hide();
25453 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25454 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25456 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25457 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25458 this.thumbEl.hide();
25460 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25461 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25463 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25464 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25465 this.errorEl.hide();
25467 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25468 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25469 this.footerEl.hide();
25471 this.setThumbBoxSize();
25477 this.fireEvent('initial', this);
25484 window.addEventListener("resize", function() { _this.resize(); } );
25486 this.bodyEl.on('click', this.beforeSelectFile, this);
25489 this.bodyEl.on('touchstart', this.onTouchStart, this);
25490 this.bodyEl.on('touchmove', this.onTouchMove, this);
25491 this.bodyEl.on('touchend', this.onTouchEnd, this);
25495 this.bodyEl.on('mousedown', this.onMouseDown, this);
25496 this.bodyEl.on('mousemove', this.onMouseMove, this);
25497 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25498 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25499 Roo.get(document).on('mouseup', this.onMouseUp, this);
25502 this.selectorEl.on('change', this.onFileSelected, this);
25508 this.baseScale = 1;
25510 this.baseRotate = 1;
25511 this.dragable = false;
25512 this.pinching = false;
25515 this.cropData = false;
25516 this.notifyEl.dom.innerHTML = this.emptyText;
25518 this.selectorEl.dom.value = '';
25522 resize : function()
25524 if(this.fireEvent('resize', this) != false){
25525 this.setThumbBoxPosition();
25526 this.setCanvasPosition();
25530 onFooterButtonClick : function(e, el, o, type)
25533 case 'rotate-left' :
25534 this.onRotateLeft(e);
25536 case 'rotate-right' :
25537 this.onRotateRight(e);
25540 this.beforeSelectFile(e);
25555 this.fireEvent('footerbuttonclick', this, type);
25558 beforeSelectFile : function(e)
25560 e.preventDefault();
25562 if(this.fireEvent('beforeselectfile', this) != false){
25563 this.selectorEl.dom.click();
25567 onFileSelected : function(e)
25569 e.preventDefault();
25571 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25575 var file = this.selectorEl.dom.files[0];
25577 if(this.fireEvent('inspect', this, file) != false){
25578 this.prepare(file);
25583 trash : function(e)
25585 this.fireEvent('trash', this);
25588 download : function(e)
25590 this.fireEvent('download', this);
25593 loadCanvas : function(src)
25595 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25599 this.imageEl = document.createElement('img');
25603 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25605 this.imageEl.src = src;
25609 onLoadCanvas : function()
25611 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25612 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25614 this.bodyEl.un('click', this.beforeSelectFile, this);
25616 this.notifyEl.hide();
25617 this.thumbEl.show();
25618 this.footerEl.show();
25620 this.baseRotateLevel();
25622 if(this.isDocument){
25623 this.setThumbBoxSize();
25626 this.setThumbBoxPosition();
25628 this.baseScaleLevel();
25634 this.canvasLoaded = true;
25637 this.maskEl.unmask();
25642 setCanvasPosition : function()
25644 if(!this.canvasEl){
25648 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25649 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25651 this.previewEl.setLeft(pw);
25652 this.previewEl.setTop(ph);
25656 onMouseDown : function(e)
25660 this.dragable = true;
25661 this.pinching = false;
25663 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25664 this.dragable = false;
25668 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25669 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25673 onMouseMove : function(e)
25677 if(!this.canvasLoaded){
25681 if (!this.dragable){
25685 var minX = Math.ceil(this.thumbEl.getLeft(true));
25686 var minY = Math.ceil(this.thumbEl.getTop(true));
25688 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25689 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25691 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25692 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25694 x = x - this.mouseX;
25695 y = y - this.mouseY;
25697 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25698 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25700 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25701 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25703 this.previewEl.setLeft(bgX);
25704 this.previewEl.setTop(bgY);
25706 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25707 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25710 onMouseUp : function(e)
25714 this.dragable = false;
25717 onMouseWheel : function(e)
25721 this.startScale = this.scale;
25723 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25725 if(!this.zoomable()){
25726 this.scale = this.startScale;
25735 zoomable : function()
25737 var minScale = this.thumbEl.getWidth() / this.minWidth;
25739 if(this.minWidth < this.minHeight){
25740 minScale = this.thumbEl.getHeight() / this.minHeight;
25743 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25744 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25748 (this.rotate == 0 || this.rotate == 180) &&
25750 width > this.imageEl.OriginWidth ||
25751 height > this.imageEl.OriginHeight ||
25752 (width < this.minWidth && height < this.minHeight)
25760 (this.rotate == 90 || this.rotate == 270) &&
25762 width > this.imageEl.OriginWidth ||
25763 height > this.imageEl.OriginHeight ||
25764 (width < this.minHeight && height < this.minWidth)
25771 !this.isDocument &&
25772 (this.rotate == 0 || this.rotate == 180) &&
25774 width < this.minWidth ||
25775 width > this.imageEl.OriginWidth ||
25776 height < this.minHeight ||
25777 height > this.imageEl.OriginHeight
25784 !this.isDocument &&
25785 (this.rotate == 90 || this.rotate == 270) &&
25787 width < this.minHeight ||
25788 width > this.imageEl.OriginWidth ||
25789 height < this.minWidth ||
25790 height > this.imageEl.OriginHeight
25800 onRotateLeft : function(e)
25802 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25804 var minScale = this.thumbEl.getWidth() / this.minWidth;
25806 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25807 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25809 this.startScale = this.scale;
25811 while (this.getScaleLevel() < minScale){
25813 this.scale = this.scale + 1;
25815 if(!this.zoomable()){
25820 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25821 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25826 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25833 this.scale = this.startScale;
25835 this.onRotateFail();
25840 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25842 if(this.isDocument){
25843 this.setThumbBoxSize();
25844 this.setThumbBoxPosition();
25845 this.setCanvasPosition();
25850 this.fireEvent('rotate', this, 'left');
25854 onRotateRight : function(e)
25856 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25858 var minScale = this.thumbEl.getWidth() / this.minWidth;
25860 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25861 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25863 this.startScale = this.scale;
25865 while (this.getScaleLevel() < minScale){
25867 this.scale = this.scale + 1;
25869 if(!this.zoomable()){
25874 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25875 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25880 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25887 this.scale = this.startScale;
25889 this.onRotateFail();
25894 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25896 if(this.isDocument){
25897 this.setThumbBoxSize();
25898 this.setThumbBoxPosition();
25899 this.setCanvasPosition();
25904 this.fireEvent('rotate', this, 'right');
25907 onRotateFail : function()
25909 this.errorEl.show(true);
25913 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25918 this.previewEl.dom.innerHTML = '';
25920 var canvasEl = document.createElement("canvas");
25922 var contextEl = canvasEl.getContext("2d");
25924 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25925 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25926 var center = this.imageEl.OriginWidth / 2;
25928 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25929 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25930 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25931 center = this.imageEl.OriginHeight / 2;
25934 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25936 contextEl.translate(center, center);
25937 contextEl.rotate(this.rotate * Math.PI / 180);
25939 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25941 this.canvasEl = document.createElement("canvas");
25943 this.contextEl = this.canvasEl.getContext("2d");
25945 switch (this.rotate) {
25948 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25949 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25951 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25956 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25957 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25959 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25960 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);
25964 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25969 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25970 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25972 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25973 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);
25977 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);
25982 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25983 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25985 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25986 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25990 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);
25997 this.previewEl.appendChild(this.canvasEl);
25999 this.setCanvasPosition();
26004 if(!this.canvasLoaded){
26008 var imageCanvas = document.createElement("canvas");
26010 var imageContext = imageCanvas.getContext("2d");
26012 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26013 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26015 var center = imageCanvas.width / 2;
26017 imageContext.translate(center, center);
26019 imageContext.rotate(this.rotate * Math.PI / 180);
26021 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26023 var canvas = document.createElement("canvas");
26025 var context = canvas.getContext("2d");
26027 canvas.width = this.minWidth;
26028 canvas.height = this.minHeight;
26030 switch (this.rotate) {
26033 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26034 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26036 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26037 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26039 var targetWidth = this.minWidth - 2 * x;
26040 var targetHeight = this.minHeight - 2 * y;
26044 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26045 scale = targetWidth / width;
26048 if(x > 0 && y == 0){
26049 scale = targetHeight / height;
26052 if(x > 0 && y > 0){
26053 scale = targetWidth / width;
26055 if(width < height){
26056 scale = targetHeight / height;
26060 context.scale(scale, scale);
26062 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26063 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26065 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26066 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26068 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26073 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26074 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26076 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26077 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26079 var targetWidth = this.minWidth - 2 * x;
26080 var targetHeight = this.minHeight - 2 * y;
26084 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26085 scale = targetWidth / width;
26088 if(x > 0 && y == 0){
26089 scale = targetHeight / height;
26092 if(x > 0 && y > 0){
26093 scale = targetWidth / width;
26095 if(width < height){
26096 scale = targetHeight / height;
26100 context.scale(scale, scale);
26102 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26103 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26105 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26106 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26108 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26110 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26115 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26116 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26118 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26119 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26121 var targetWidth = this.minWidth - 2 * x;
26122 var targetHeight = this.minHeight - 2 * y;
26126 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26127 scale = targetWidth / width;
26130 if(x > 0 && y == 0){
26131 scale = targetHeight / height;
26134 if(x > 0 && y > 0){
26135 scale = targetWidth / width;
26137 if(width < height){
26138 scale = targetHeight / height;
26142 context.scale(scale, scale);
26144 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26145 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26147 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26148 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26150 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26151 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26153 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26158 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26159 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26161 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26162 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26164 var targetWidth = this.minWidth - 2 * x;
26165 var targetHeight = this.minHeight - 2 * y;
26169 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26170 scale = targetWidth / width;
26173 if(x > 0 && y == 0){
26174 scale = targetHeight / height;
26177 if(x > 0 && y > 0){
26178 scale = targetWidth / width;
26180 if(width < height){
26181 scale = targetHeight / height;
26185 context.scale(scale, scale);
26187 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26188 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26190 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26191 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26193 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26195 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26202 this.cropData = canvas.toDataURL(this.cropType);
26204 if(this.fireEvent('crop', this, this.cropData) !== false){
26205 this.process(this.file, this.cropData);
26212 setThumbBoxSize : function()
26216 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26217 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26218 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26220 this.minWidth = width;
26221 this.minHeight = height;
26223 if(this.rotate == 90 || this.rotate == 270){
26224 this.minWidth = height;
26225 this.minHeight = width;
26230 width = Math.ceil(this.minWidth * height / this.minHeight);
26232 if(this.minWidth > this.minHeight){
26234 height = Math.ceil(this.minHeight * width / this.minWidth);
26237 this.thumbEl.setStyle({
26238 width : width + 'px',
26239 height : height + 'px'
26246 setThumbBoxPosition : function()
26248 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26249 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26251 this.thumbEl.setLeft(x);
26252 this.thumbEl.setTop(y);
26256 baseRotateLevel : function()
26258 this.baseRotate = 1;
26261 typeof(this.exif) != 'undefined' &&
26262 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26263 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26265 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26268 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26272 baseScaleLevel : function()
26276 if(this.isDocument){
26278 if(this.baseRotate == 6 || this.baseRotate == 8){
26280 height = this.thumbEl.getHeight();
26281 this.baseScale = height / this.imageEl.OriginWidth;
26283 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26284 width = this.thumbEl.getWidth();
26285 this.baseScale = width / this.imageEl.OriginHeight;
26291 height = this.thumbEl.getHeight();
26292 this.baseScale = height / this.imageEl.OriginHeight;
26294 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26295 width = this.thumbEl.getWidth();
26296 this.baseScale = width / this.imageEl.OriginWidth;
26302 if(this.baseRotate == 6 || this.baseRotate == 8){
26304 width = this.thumbEl.getHeight();
26305 this.baseScale = width / this.imageEl.OriginHeight;
26307 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26308 height = this.thumbEl.getWidth();
26309 this.baseScale = height / this.imageEl.OriginHeight;
26312 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26313 height = this.thumbEl.getWidth();
26314 this.baseScale = height / this.imageEl.OriginHeight;
26316 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26317 width = this.thumbEl.getHeight();
26318 this.baseScale = width / this.imageEl.OriginWidth;
26325 width = this.thumbEl.getWidth();
26326 this.baseScale = width / this.imageEl.OriginWidth;
26328 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26329 height = this.thumbEl.getHeight();
26330 this.baseScale = height / this.imageEl.OriginHeight;
26333 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26335 height = this.thumbEl.getHeight();
26336 this.baseScale = height / this.imageEl.OriginHeight;
26338 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26339 width = this.thumbEl.getWidth();
26340 this.baseScale = width / this.imageEl.OriginWidth;
26348 getScaleLevel : function()
26350 return this.baseScale * Math.pow(1.1, this.scale);
26353 onTouchStart : function(e)
26355 if(!this.canvasLoaded){
26356 this.beforeSelectFile(e);
26360 var touches = e.browserEvent.touches;
26366 if(touches.length == 1){
26367 this.onMouseDown(e);
26371 if(touches.length != 2){
26377 for(var i = 0, finger; finger = touches[i]; i++){
26378 coords.push(finger.pageX, finger.pageY);
26381 var x = Math.pow(coords[0] - coords[2], 2);
26382 var y = Math.pow(coords[1] - coords[3], 2);
26384 this.startDistance = Math.sqrt(x + y);
26386 this.startScale = this.scale;
26388 this.pinching = true;
26389 this.dragable = false;
26393 onTouchMove : function(e)
26395 if(!this.pinching && !this.dragable){
26399 var touches = e.browserEvent.touches;
26406 this.onMouseMove(e);
26412 for(var i = 0, finger; finger = touches[i]; i++){
26413 coords.push(finger.pageX, finger.pageY);
26416 var x = Math.pow(coords[0] - coords[2], 2);
26417 var y = Math.pow(coords[1] - coords[3], 2);
26419 this.endDistance = Math.sqrt(x + y);
26421 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26423 if(!this.zoomable()){
26424 this.scale = this.startScale;
26432 onTouchEnd : function(e)
26434 this.pinching = false;
26435 this.dragable = false;
26439 process : function(file, crop)
26442 this.maskEl.mask(this.loadingText);
26445 this.xhr = new XMLHttpRequest();
26447 file.xhr = this.xhr;
26449 this.xhr.open(this.method, this.url, true);
26452 "Accept": "application/json",
26453 "Cache-Control": "no-cache",
26454 "X-Requested-With": "XMLHttpRequest"
26457 for (var headerName in headers) {
26458 var headerValue = headers[headerName];
26460 this.xhr.setRequestHeader(headerName, headerValue);
26466 this.xhr.onload = function()
26468 _this.xhrOnLoad(_this.xhr);
26471 this.xhr.onerror = function()
26473 _this.xhrOnError(_this.xhr);
26476 var formData = new FormData();
26478 formData.append('returnHTML', 'NO');
26481 formData.append('crop', crop);
26484 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26485 formData.append(this.paramName, file, file.name);
26488 if(typeof(file.filename) != 'undefined'){
26489 formData.append('filename', file.filename);
26492 if(typeof(file.mimetype) != 'undefined'){
26493 formData.append('mimetype', file.mimetype);
26496 if(this.fireEvent('arrange', this, formData) != false){
26497 this.xhr.send(formData);
26501 xhrOnLoad : function(xhr)
26504 this.maskEl.unmask();
26507 if (xhr.readyState !== 4) {
26508 this.fireEvent('exception', this, xhr);
26512 var response = Roo.decode(xhr.responseText);
26514 if(!response.success){
26515 this.fireEvent('exception', this, xhr);
26519 var response = Roo.decode(xhr.responseText);
26521 this.fireEvent('upload', this, response);
26525 xhrOnError : function()
26528 this.maskEl.unmask();
26531 Roo.log('xhr on error');
26533 var response = Roo.decode(xhr.responseText);
26539 prepare : function(file)
26542 this.maskEl.mask(this.loadingText);
26548 if(typeof(file) === 'string'){
26549 this.loadCanvas(file);
26553 if(!file || !this.urlAPI){
26558 this.cropType = file.type;
26562 if(this.fireEvent('prepare', this, this.file) != false){
26564 var reader = new FileReader();
26566 reader.onload = function (e) {
26567 if (e.target.error) {
26568 Roo.log(e.target.error);
26572 var buffer = e.target.result,
26573 dataView = new DataView(buffer),
26575 maxOffset = dataView.byteLength - 4,
26579 if (dataView.getUint16(0) === 0xffd8) {
26580 while (offset < maxOffset) {
26581 markerBytes = dataView.getUint16(offset);
26583 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26584 markerLength = dataView.getUint16(offset + 2) + 2;
26585 if (offset + markerLength > dataView.byteLength) {
26586 Roo.log('Invalid meta data: Invalid segment size.');
26590 if(markerBytes == 0xffe1){
26591 _this.parseExifData(
26598 offset += markerLength;
26608 var url = _this.urlAPI.createObjectURL(_this.file);
26610 _this.loadCanvas(url);
26615 reader.readAsArrayBuffer(this.file);
26621 parseExifData : function(dataView, offset, length)
26623 var tiffOffset = offset + 10,
26627 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26628 // No Exif data, might be XMP data instead
26632 // Check for the ASCII code for "Exif" (0x45786966):
26633 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26634 // No Exif data, might be XMP data instead
26637 if (tiffOffset + 8 > dataView.byteLength) {
26638 Roo.log('Invalid Exif data: Invalid segment size.');
26641 // Check for the two null bytes:
26642 if (dataView.getUint16(offset + 8) !== 0x0000) {
26643 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26646 // Check the byte alignment:
26647 switch (dataView.getUint16(tiffOffset)) {
26649 littleEndian = true;
26652 littleEndian = false;
26655 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26658 // Check for the TIFF tag marker (0x002A):
26659 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26660 Roo.log('Invalid Exif data: Missing TIFF marker.');
26663 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26664 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26666 this.parseExifTags(
26669 tiffOffset + dirOffset,
26674 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26679 if (dirOffset + 6 > dataView.byteLength) {
26680 Roo.log('Invalid Exif data: Invalid directory offset.');
26683 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26684 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26685 if (dirEndOffset + 4 > dataView.byteLength) {
26686 Roo.log('Invalid Exif data: Invalid directory size.');
26689 for (i = 0; i < tagsNumber; i += 1) {
26693 dirOffset + 2 + 12 * i, // tag offset
26697 // Return the offset to the next directory:
26698 return dataView.getUint32(dirEndOffset, littleEndian);
26701 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26703 var tag = dataView.getUint16(offset, littleEndian);
26705 this.exif[tag] = this.getExifValue(
26709 dataView.getUint16(offset + 2, littleEndian), // tag type
26710 dataView.getUint32(offset + 4, littleEndian), // tag length
26715 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26717 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26726 Roo.log('Invalid Exif data: Invalid tag type.');
26730 tagSize = tagType.size * length;
26731 // Determine if the value is contained in the dataOffset bytes,
26732 // or if the value at the dataOffset is a pointer to the actual data:
26733 dataOffset = tagSize > 4 ?
26734 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26735 if (dataOffset + tagSize > dataView.byteLength) {
26736 Roo.log('Invalid Exif data: Invalid data offset.');
26739 if (length === 1) {
26740 return tagType.getValue(dataView, dataOffset, littleEndian);
26743 for (i = 0; i < length; i += 1) {
26744 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26747 if (tagType.ascii) {
26749 // Concatenate the chars:
26750 for (i = 0; i < values.length; i += 1) {
26752 // Ignore the terminating NULL byte(s):
26753 if (c === '\u0000') {
26765 Roo.apply(Roo.bootstrap.UploadCropbox, {
26767 'Orientation': 0x0112
26771 1: 0, //'top-left',
26773 3: 180, //'bottom-right',
26774 // 4: 'bottom-left',
26776 6: 90, //'right-top',
26777 // 7: 'right-bottom',
26778 8: 270 //'left-bottom'
26782 // byte, 8-bit unsigned int:
26784 getValue: function (dataView, dataOffset) {
26785 return dataView.getUint8(dataOffset);
26789 // ascii, 8-bit byte:
26791 getValue: function (dataView, dataOffset) {
26792 return String.fromCharCode(dataView.getUint8(dataOffset));
26797 // short, 16 bit int:
26799 getValue: function (dataView, dataOffset, littleEndian) {
26800 return dataView.getUint16(dataOffset, littleEndian);
26804 // long, 32 bit int:
26806 getValue: function (dataView, dataOffset, littleEndian) {
26807 return dataView.getUint32(dataOffset, littleEndian);
26811 // rational = two long values, first is numerator, second is denominator:
26813 getValue: function (dataView, dataOffset, littleEndian) {
26814 return dataView.getUint32(dataOffset, littleEndian) /
26815 dataView.getUint32(dataOffset + 4, littleEndian);
26819 // slong, 32 bit signed int:
26821 getValue: function (dataView, dataOffset, littleEndian) {
26822 return dataView.getInt32(dataOffset, littleEndian);
26826 // srational, two slongs, first is numerator, second is denominator:
26828 getValue: function (dataView, dataOffset, littleEndian) {
26829 return dataView.getInt32(dataOffset, littleEndian) /
26830 dataView.getInt32(dataOffset + 4, littleEndian);
26840 cls : 'btn-group roo-upload-cropbox-rotate-left',
26841 action : 'rotate-left',
26845 cls : 'btn btn-default',
26846 html : '<i class="fa fa-undo"></i>'
26852 cls : 'btn-group roo-upload-cropbox-picture',
26853 action : 'picture',
26857 cls : 'btn btn-default',
26858 html : '<i class="fa fa-picture-o"></i>'
26864 cls : 'btn-group roo-upload-cropbox-rotate-right',
26865 action : 'rotate-right',
26869 cls : 'btn btn-default',
26870 html : '<i class="fa fa-repeat"></i>'
26878 cls : 'btn-group roo-upload-cropbox-rotate-left',
26879 action : 'rotate-left',
26883 cls : 'btn btn-default',
26884 html : '<i class="fa fa-undo"></i>'
26890 cls : 'btn-group roo-upload-cropbox-download',
26891 action : 'download',
26895 cls : 'btn btn-default',
26896 html : '<i class="fa fa-download"></i>'
26902 cls : 'btn-group roo-upload-cropbox-crop',
26907 cls : 'btn btn-default',
26908 html : '<i class="fa fa-crop"></i>'
26914 cls : 'btn-group roo-upload-cropbox-trash',
26919 cls : 'btn btn-default',
26920 html : '<i class="fa fa-trash"></i>'
26926 cls : 'btn-group roo-upload-cropbox-rotate-right',
26927 action : 'rotate-right',
26931 cls : 'btn btn-default',
26932 html : '<i class="fa fa-repeat"></i>'
26940 cls : 'btn-group roo-upload-cropbox-rotate-left',
26941 action : 'rotate-left',
26945 cls : 'btn btn-default',
26946 html : '<i class="fa fa-undo"></i>'
26952 cls : 'btn-group roo-upload-cropbox-rotate-right',
26953 action : 'rotate-right',
26957 cls : 'btn btn-default',
26958 html : '<i class="fa fa-repeat"></i>'
26971 * @class Roo.bootstrap.DocumentManager
26972 * @extends Roo.bootstrap.Component
26973 * Bootstrap DocumentManager class
26974 * @cfg {String} paramName default 'imageUpload'
26975 * @cfg {String} method default POST
26976 * @cfg {String} url action url
26977 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26978 * @cfg {Boolean} multiple multiple upload default true
26979 * @cfg {Number} thumbSize default 300
26980 * @cfg {String} fieldLabel
26981 * @cfg {Number} labelWidth default 4
26982 * @cfg {String} labelAlign (left|top) default left
26983 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26986 * Create a new DocumentManager
26987 * @param {Object} config The config object
26990 Roo.bootstrap.DocumentManager = function(config){
26991 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26996 * Fire when initial the DocumentManager
26997 * @param {Roo.bootstrap.DocumentManager} this
27002 * inspect selected file
27003 * @param {Roo.bootstrap.DocumentManager} this
27004 * @param {File} file
27009 * Fire when xhr load exception
27010 * @param {Roo.bootstrap.DocumentManager} this
27011 * @param {XMLHttpRequest} xhr
27013 "exception" : true,
27016 * prepare the form data
27017 * @param {Roo.bootstrap.DocumentManager} this
27018 * @param {Object} formData
27023 * Fire when remove the file
27024 * @param {Roo.bootstrap.DocumentManager} this
27025 * @param {Object} file
27030 * Fire after refresh the file
27031 * @param {Roo.bootstrap.DocumentManager} this
27036 * Fire after click the image
27037 * @param {Roo.bootstrap.DocumentManager} this
27038 * @param {Object} file
27043 * Fire when upload a image and editable set to true
27044 * @param {Roo.bootstrap.DocumentManager} this
27045 * @param {Object} file
27049 * @event beforeselectfile
27050 * Fire before select file
27051 * @param {Roo.bootstrap.DocumentManager} this
27053 "beforeselectfile" : true,
27056 * Fire before process file
27057 * @param {Roo.bootstrap.DocumentManager} this
27058 * @param {Object} file
27065 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27074 paramName : 'imageUpload',
27077 labelAlign : 'left',
27084 getAutoCreate : function()
27086 var managerWidget = {
27088 cls : 'roo-document-manager',
27092 cls : 'roo-document-manager-selector',
27097 cls : 'roo-document-manager-uploader',
27101 cls : 'roo-document-manager-upload-btn',
27102 html : '<i class="fa fa-plus"></i>'
27113 cls : 'column col-md-12',
27118 if(this.fieldLabel.length){
27123 cls : 'column col-md-12',
27124 html : this.fieldLabel
27128 cls : 'column col-md-12',
27133 if(this.labelAlign == 'left'){
27137 cls : 'column col-md-' + this.labelWidth,
27138 html : this.fieldLabel
27142 cls : 'column col-md-' + (12 - this.labelWidth),
27152 cls : 'row clearfix',
27160 initEvents : function()
27162 this.managerEl = this.el.select('.roo-document-manager', true).first();
27163 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27165 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27166 this.selectorEl.hide();
27169 this.selectorEl.attr('multiple', 'multiple');
27172 this.selectorEl.on('change', this.onFileSelected, this);
27174 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27175 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27177 this.uploader.on('click', this.onUploaderClick, this);
27179 this.renderProgressDialog();
27183 window.addEventListener("resize", function() { _this.refresh(); } );
27185 this.fireEvent('initial', this);
27188 renderProgressDialog : function()
27192 this.progressDialog = new Roo.bootstrap.Modal({
27193 cls : 'roo-document-manager-progress-dialog',
27194 allow_close : false,
27204 btnclick : function() {
27205 _this.uploadCancel();
27211 this.progressDialog.render(Roo.get(document.body));
27213 this.progress = new Roo.bootstrap.Progress({
27214 cls : 'roo-document-manager-progress',
27219 this.progress.render(this.progressDialog.getChildContainer());
27221 this.progressBar = new Roo.bootstrap.ProgressBar({
27222 cls : 'roo-document-manager-progress-bar',
27225 aria_valuemax : 12,
27229 this.progressBar.render(this.progress.getChildContainer());
27232 onUploaderClick : function(e)
27234 e.preventDefault();
27236 if(this.fireEvent('beforeselectfile', this) != false){
27237 this.selectorEl.dom.click();
27242 onFileSelected : function(e)
27244 e.preventDefault();
27246 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27250 Roo.each(this.selectorEl.dom.files, function(file){
27251 if(this.fireEvent('inspect', this, file) != false){
27252 this.files.push(file);
27262 this.selectorEl.dom.value = '';
27264 if(!this.files.length){
27268 if(this.boxes > 0 && this.files.length > this.boxes){
27269 this.files = this.files.slice(0, this.boxes);
27272 this.uploader.show();
27274 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27275 this.uploader.hide();
27284 Roo.each(this.files, function(file){
27286 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27287 var f = this.renderPreview(file);
27292 if(file.type.indexOf('image') != -1){
27293 this.delegates.push(
27295 _this.process(file);
27296 }).createDelegate(this)
27304 _this.process(file);
27305 }).createDelegate(this)
27310 this.files = files;
27312 this.delegates = this.delegates.concat(docs);
27314 if(!this.delegates.length){
27319 this.progressBar.aria_valuemax = this.delegates.length;
27326 arrange : function()
27328 if(!this.delegates.length){
27329 this.progressDialog.hide();
27334 var delegate = this.delegates.shift();
27336 this.progressDialog.show();
27338 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27340 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27345 refresh : function()
27347 this.uploader.show();
27349 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27350 this.uploader.hide();
27353 Roo.isTouch ? this.closable(false) : this.closable(true);
27355 this.fireEvent('refresh', this);
27358 onRemove : function(e, el, o)
27360 e.preventDefault();
27362 this.fireEvent('remove', this, o);
27366 remove : function(o)
27370 Roo.each(this.files, function(file){
27371 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27380 this.files = files;
27387 Roo.each(this.files, function(file){
27392 file.target.remove();
27401 onClick : function(e, el, o)
27403 e.preventDefault();
27405 this.fireEvent('click', this, o);
27409 closable : function(closable)
27411 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27413 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27425 xhrOnLoad : function(xhr)
27427 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27431 if (xhr.readyState !== 4) {
27433 this.fireEvent('exception', this, xhr);
27437 var response = Roo.decode(xhr.responseText);
27439 if(!response.success){
27441 this.fireEvent('exception', this, xhr);
27445 var file = this.renderPreview(response.data);
27447 this.files.push(file);
27453 xhrOnError : function(xhr)
27455 Roo.log('xhr on error');
27457 var response = Roo.decode(xhr.responseText);
27464 process : function(file)
27466 if(this.fireEvent('process', this, file) !== false){
27467 if(this.editable && file.type.indexOf('image') != -1){
27468 this.fireEvent('edit', this, file);
27472 this.uploadStart(file, false);
27479 uploadStart : function(file, crop)
27481 this.xhr = new XMLHttpRequest();
27483 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27488 file.xhr = this.xhr;
27490 this.managerEl.createChild({
27492 cls : 'roo-document-manager-loading',
27496 tooltip : file.name,
27497 cls : 'roo-document-manager-thumb',
27498 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27504 this.xhr.open(this.method, this.url, true);
27507 "Accept": "application/json",
27508 "Cache-Control": "no-cache",
27509 "X-Requested-With": "XMLHttpRequest"
27512 for (var headerName in headers) {
27513 var headerValue = headers[headerName];
27515 this.xhr.setRequestHeader(headerName, headerValue);
27521 this.xhr.onload = function()
27523 _this.xhrOnLoad(_this.xhr);
27526 this.xhr.onerror = function()
27528 _this.xhrOnError(_this.xhr);
27531 var formData = new FormData();
27533 formData.append('returnHTML', 'NO');
27536 formData.append('crop', crop);
27539 formData.append(this.paramName, file, file.name);
27541 if(this.fireEvent('prepare', this, formData) != false){
27542 this.xhr.send(formData);
27546 uploadCancel : function()
27553 this.delegates = [];
27555 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27562 renderPreview : function(file)
27564 if(typeof(file.target) != 'undefined' && file.target){
27568 var previewEl = this.managerEl.createChild({
27570 cls : 'roo-document-manager-preview',
27574 tooltip : file.filename,
27575 cls : 'roo-document-manager-thumb',
27576 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27581 html : '<i class="fa fa-times-circle"></i>'
27586 var close = previewEl.select('button.close', true).first();
27588 close.on('click', this.onRemove, this, file);
27590 file.target = previewEl;
27592 var image = previewEl.select('img', true).first();
27596 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27598 image.on('click', this.onClick, this, file);
27604 onPreviewLoad : function(file, image)
27606 if(typeof(file.target) == 'undefined' || !file.target){
27610 var width = image.dom.naturalWidth || image.dom.width;
27611 var height = image.dom.naturalHeight || image.dom.height;
27613 if(width > height){
27614 file.target.addClass('wide');
27618 file.target.addClass('tall');
27623 uploadFromSource : function(file, crop)
27625 this.xhr = new XMLHttpRequest();
27627 this.managerEl.createChild({
27629 cls : 'roo-document-manager-loading',
27633 tooltip : file.name,
27634 cls : 'roo-document-manager-thumb',
27635 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27641 this.xhr.open(this.method, this.url, true);
27644 "Accept": "application/json",
27645 "Cache-Control": "no-cache",
27646 "X-Requested-With": "XMLHttpRequest"
27649 for (var headerName in headers) {
27650 var headerValue = headers[headerName];
27652 this.xhr.setRequestHeader(headerName, headerValue);
27658 this.xhr.onload = function()
27660 _this.xhrOnLoad(_this.xhr);
27663 this.xhr.onerror = function()
27665 _this.xhrOnError(_this.xhr);
27668 var formData = new FormData();
27670 formData.append('returnHTML', 'NO');
27672 formData.append('crop', crop);
27674 if(typeof(file.filename) != 'undefined'){
27675 formData.append('filename', file.filename);
27678 if(typeof(file.mimetype) != 'undefined'){
27679 formData.append('mimetype', file.mimetype);
27682 if(this.fireEvent('prepare', this, formData) != false){
27683 this.xhr.send(formData);
27693 * @class Roo.bootstrap.DocumentViewer
27694 * @extends Roo.bootstrap.Component
27695 * Bootstrap DocumentViewer class
27698 * Create a new DocumentViewer
27699 * @param {Object} config The config object
27702 Roo.bootstrap.DocumentViewer = function(config){
27703 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27708 * Fire after initEvent
27709 * @param {Roo.bootstrap.DocumentViewer} this
27715 * @param {Roo.bootstrap.DocumentViewer} this
27720 * Fire after trash button
27721 * @param {Roo.bootstrap.DocumentViewer} this
27728 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27730 getAutoCreate : function()
27734 cls : 'roo-document-viewer',
27738 cls : 'roo-document-viewer-body',
27742 cls : 'roo-document-viewer-thumb',
27746 cls : 'roo-document-viewer-image'
27754 cls : 'roo-document-viewer-footer',
27757 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27765 cls : 'btn btn-default roo-document-viewer-trash',
27766 html : '<i class="fa fa-trash"></i>'
27779 initEvents : function()
27782 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27783 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27785 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27786 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27788 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27789 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27791 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27792 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27794 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27795 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27797 this.bodyEl.on('click', this.onClick, this);
27799 this.trashBtn.on('click', this.onTrash, this);
27803 initial : function()
27805 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27808 this.fireEvent('initial', this);
27812 onClick : function(e)
27814 e.preventDefault();
27816 this.fireEvent('click', this);
27819 onTrash : function(e)
27821 e.preventDefault();
27823 this.fireEvent('trash', this);
27835 * @class Roo.bootstrap.NavProgressBar
27836 * @extends Roo.bootstrap.Component
27837 * Bootstrap NavProgressBar class
27840 * Create a new nav progress bar
27841 * @param {Object} config The config object
27844 Roo.bootstrap.NavProgressBar = function(config){
27845 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27847 this.bullets = this.bullets || [];
27849 // Roo.bootstrap.NavProgressBar.register(this);
27853 * Fires when the active item changes
27854 * @param {Roo.bootstrap.NavProgressBar} this
27855 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27856 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27863 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27868 getAutoCreate : function()
27870 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27874 cls : 'roo-navigation-bar-group',
27878 cls : 'roo-navigation-top-bar'
27882 cls : 'roo-navigation-bullets-bar',
27886 cls : 'roo-navigation-bar'
27893 cls : 'roo-navigation-bottom-bar'
27903 initEvents: function()
27908 onRender : function(ct, position)
27910 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27912 if(this.bullets.length){
27913 Roo.each(this.bullets, function(b){
27922 addItem : function(cfg)
27924 var item = new Roo.bootstrap.NavProgressItem(cfg);
27926 item.parentId = this.id;
27927 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27930 var top = new Roo.bootstrap.Element({
27932 cls : 'roo-navigation-bar-text'
27935 var bottom = new Roo.bootstrap.Element({
27937 cls : 'roo-navigation-bar-text'
27940 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27941 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27943 var topText = new Roo.bootstrap.Element({
27945 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27948 var bottomText = new Roo.bootstrap.Element({
27950 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27953 topText.onRender(top.el, null);
27954 bottomText.onRender(bottom.el, null);
27957 item.bottomEl = bottom;
27960 this.barItems.push(item);
27965 getActive : function()
27967 var active = false;
27969 Roo.each(this.barItems, function(v){
27971 if (!v.isActive()) {
27983 setActiveItem : function(item)
27987 Roo.each(this.barItems, function(v){
27988 if (v.rid == item.rid) {
27992 if (v.isActive()) {
27993 v.setActive(false);
27998 item.setActive(true);
28000 this.fireEvent('changed', this, item, prev);
28003 getBarItem: function(rid)
28007 Roo.each(this.barItems, function(e) {
28008 if (e.rid != rid) {
28019 indexOfItem : function(item)
28023 Roo.each(this.barItems, function(v, i){
28025 if (v.rid != item.rid) {
28036 setActiveNext : function()
28038 var i = this.indexOfItem(this.getActive());
28040 if (i > this.barItems.length) {
28044 this.setActiveItem(this.barItems[i+1]);
28047 setActivePrev : function()
28049 var i = this.indexOfItem(this.getActive());
28055 this.setActiveItem(this.barItems[i-1]);
28058 format : function()
28060 if(!this.barItems.length){
28064 var width = 100 / this.barItems.length;
28066 Roo.each(this.barItems, function(i){
28067 i.el.setStyle('width', width + '%');
28068 i.topEl.el.setStyle('width', width + '%');
28069 i.bottomEl.el.setStyle('width', width + '%');
28078 * Nav Progress Item
28083 * @class Roo.bootstrap.NavProgressItem
28084 * @extends Roo.bootstrap.Component
28085 * Bootstrap NavProgressItem class
28086 * @cfg {String} rid the reference id
28087 * @cfg {Boolean} active (true|false) Is item active default false
28088 * @cfg {Boolean} disabled (true|false) Is item active default false
28089 * @cfg {String} html
28090 * @cfg {String} position (top|bottom) text position default bottom
28091 * @cfg {String} icon show icon instead of number
28094 * Create a new NavProgressItem
28095 * @param {Object} config The config object
28097 Roo.bootstrap.NavProgressItem = function(config){
28098 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28103 * The raw click event for the entire grid.
28104 * @param {Roo.bootstrap.NavProgressItem} this
28105 * @param {Roo.EventObject} e
28112 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28118 position : 'bottom',
28121 getAutoCreate : function()
28123 var iconCls = 'roo-navigation-bar-item-icon';
28125 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28129 cls: 'roo-navigation-bar-item',
28139 cfg.cls += ' active';
28142 cfg.cls += ' disabled';
28148 disable : function()
28150 this.setDisabled(true);
28153 enable : function()
28155 this.setDisabled(false);
28158 initEvents: function()
28160 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28162 this.iconEl.on('click', this.onClick, this);
28165 onClick : function(e)
28167 e.preventDefault();
28173 if(this.fireEvent('click', this, e) === false){
28177 this.parent().setActiveItem(this);
28180 isActive: function ()
28182 return this.active;
28185 setActive : function(state)
28187 if(this.active == state){
28191 this.active = state;
28194 this.el.addClass('active');
28198 this.el.removeClass('active');
28203 setDisabled : function(state)
28205 if(this.disabled == state){
28209 this.disabled = state;
28212 this.el.addClass('disabled');
28216 this.el.removeClass('disabled');
28219 tooltipEl : function()
28221 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28234 * @class Roo.bootstrap.FieldLabel
28235 * @extends Roo.bootstrap.Component
28236 * Bootstrap FieldLabel class
28237 * @cfg {String} html contents of the element
28238 * @cfg {String} tag tag of the element default label
28239 * @cfg {String} cls class of the element
28240 * @cfg {String} target label target
28241 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28242 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28243 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28244 * @cfg {String} iconTooltip default "This field is required"
28247 * Create a new FieldLabel
28248 * @param {Object} config The config object
28251 Roo.bootstrap.FieldLabel = function(config){
28252 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28257 * Fires after the field has been marked as invalid.
28258 * @param {Roo.form.FieldLabel} this
28259 * @param {String} msg The validation message
28264 * Fires after the field has been validated with no errors.
28265 * @param {Roo.form.FieldLabel} this
28271 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28278 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28279 validClass : 'text-success fa fa-lg fa-check',
28280 iconTooltip : 'This field is required',
28282 getAutoCreate : function(){
28286 cls : 'roo-bootstrap-field-label ' + this.cls,
28292 tooltip : this.iconTooltip
28304 initEvents: function()
28306 Roo.bootstrap.Element.superclass.initEvents.call(this);
28308 this.iconEl = this.el.select('i', true).first();
28310 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28312 Roo.bootstrap.FieldLabel.register(this);
28316 * Mark this field as valid
28318 markValid : function()
28320 this.iconEl.show();
28322 this.iconEl.removeClass(this.invalidClass);
28324 this.iconEl.addClass(this.validClass);
28326 this.fireEvent('valid', this);
28330 * Mark this field as invalid
28331 * @param {String} msg The validation message
28333 markInvalid : function(msg)
28335 this.iconEl.show();
28337 this.iconEl.removeClass(this.validClass);
28339 this.iconEl.addClass(this.invalidClass);
28341 this.fireEvent('invalid', this, msg);
28347 Roo.apply(Roo.bootstrap.FieldLabel, {
28352 * register a FieldLabel Group
28353 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28355 register : function(label)
28357 if(this.groups.hasOwnProperty(label.target)){
28361 this.groups[label.target] = label;
28365 * fetch a FieldLabel Group based on the target
28366 * @param {string} target
28367 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28369 get: function(target) {
28370 if (typeof(this.groups[target]) == 'undefined') {
28374 return this.groups[target] ;
28383 * page DateSplitField.
28389 * @class Roo.bootstrap.DateSplitField
28390 * @extends Roo.bootstrap.Component
28391 * Bootstrap DateSplitField class
28392 * @cfg {string} fieldLabel - the label associated
28393 * @cfg {Number} labelWidth set the width of label (0-12)
28394 * @cfg {String} labelAlign (top|left)
28395 * @cfg {Boolean} dayAllowBlank (true|false) default false
28396 * @cfg {Boolean} monthAllowBlank (true|false) default false
28397 * @cfg {Boolean} yearAllowBlank (true|false) default false
28398 * @cfg {string} dayPlaceholder
28399 * @cfg {string} monthPlaceholder
28400 * @cfg {string} yearPlaceholder
28401 * @cfg {string} dayFormat default 'd'
28402 * @cfg {string} monthFormat default 'm'
28403 * @cfg {string} yearFormat default 'Y'
28407 * Create a new DateSplitField
28408 * @param {Object} config The config object
28411 Roo.bootstrap.DateSplitField = function(config){
28412 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28418 * getting the data of years
28419 * @param {Roo.bootstrap.DateSplitField} this
28420 * @param {Object} years
28425 * getting the data of days
28426 * @param {Roo.bootstrap.DateSplitField} this
28427 * @param {Object} days
28432 * Fires after the field has been marked as invalid.
28433 * @param {Roo.form.Field} this
28434 * @param {String} msg The validation message
28439 * Fires after the field has been validated with no errors.
28440 * @param {Roo.form.Field} this
28446 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28449 labelAlign : 'top',
28451 dayAllowBlank : false,
28452 monthAllowBlank : false,
28453 yearAllowBlank : false,
28454 dayPlaceholder : '',
28455 monthPlaceholder : '',
28456 yearPlaceholder : '',
28460 isFormField : true,
28462 getAutoCreate : function()
28466 cls : 'row roo-date-split-field-group',
28471 cls : 'form-hidden-field roo-date-split-field-group-value',
28477 if(this.fieldLabel){
28480 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28484 html : this.fieldLabel
28490 Roo.each(['day', 'month', 'year'], function(t){
28493 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28500 inputEl: function ()
28502 return this.el.select('.roo-date-split-field-group-value', true).first();
28505 onRender : function(ct, position)
28509 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28511 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28513 this.dayField = new Roo.bootstrap.ComboBox({
28514 allowBlank : this.dayAllowBlank,
28515 alwaysQuery : true,
28516 displayField : 'value',
28519 forceSelection : true,
28521 placeholder : this.dayPlaceholder,
28522 selectOnFocus : true,
28523 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28524 triggerAction : 'all',
28526 valueField : 'value',
28527 store : new Roo.data.SimpleStore({
28528 data : (function() {
28530 _this.fireEvent('days', _this, days);
28533 fields : [ 'value' ]
28536 select : function (_self, record, index)
28538 _this.setValue(_this.getValue());
28543 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28545 this.monthField = new Roo.bootstrap.MonthField({
28546 after : '<i class=\"fa fa-calendar\"></i>',
28547 allowBlank : this.monthAllowBlank,
28548 placeholder : this.monthPlaceholder,
28551 render : function (_self)
28553 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28554 e.preventDefault();
28558 select : function (_self, oldvalue, newvalue)
28560 _this.setValue(_this.getValue());
28565 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28567 this.yearField = new Roo.bootstrap.ComboBox({
28568 allowBlank : this.yearAllowBlank,
28569 alwaysQuery : true,
28570 displayField : 'value',
28573 forceSelection : true,
28575 placeholder : this.yearPlaceholder,
28576 selectOnFocus : true,
28577 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28578 triggerAction : 'all',
28580 valueField : 'value',
28581 store : new Roo.data.SimpleStore({
28582 data : (function() {
28584 _this.fireEvent('years', _this, years);
28587 fields : [ 'value' ]
28590 select : function (_self, record, index)
28592 _this.setValue(_this.getValue());
28597 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28600 setValue : function(v, format)
28602 this.inputEl.dom.value = v;
28604 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28606 var d = Date.parseDate(v, f);
28613 this.setDay(d.format(this.dayFormat));
28614 this.setMonth(d.format(this.monthFormat));
28615 this.setYear(d.format(this.yearFormat));
28622 setDay : function(v)
28624 this.dayField.setValue(v);
28625 this.inputEl.dom.value = this.getValue();
28630 setMonth : function(v)
28632 this.monthField.setValue(v, true);
28633 this.inputEl.dom.value = this.getValue();
28638 setYear : function(v)
28640 this.yearField.setValue(v);
28641 this.inputEl.dom.value = this.getValue();
28646 getDay : function()
28648 return this.dayField.getValue();
28651 getMonth : function()
28653 return this.monthField.getValue();
28656 getYear : function()
28658 return this.yearField.getValue();
28661 getValue : function()
28663 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28665 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28675 this.inputEl.dom.value = '';
28680 validate : function()
28682 var d = this.dayField.validate();
28683 var m = this.monthField.validate();
28684 var y = this.yearField.validate();
28689 (!this.dayAllowBlank && !d) ||
28690 (!this.monthAllowBlank && !m) ||
28691 (!this.yearAllowBlank && !y)
28696 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28705 this.markInvalid();
28710 markValid : function()
28713 var label = this.el.select('label', true).first();
28714 var icon = this.el.select('i.fa-star', true).first();
28720 this.fireEvent('valid', this);
28724 * Mark this field as invalid
28725 * @param {String} msg The validation message
28727 markInvalid : function(msg)
28730 var label = this.el.select('label', true).first();
28731 var icon = this.el.select('i.fa-star', true).first();
28733 if(label && !icon){
28734 this.el.select('.roo-date-split-field-label', true).createChild({
28736 cls : 'text-danger fa fa-lg fa-star',
28737 tooltip : 'This field is required',
28738 style : 'margin-right:5px;'
28742 this.fireEvent('invalid', this, msg);
28745 clearInvalid : function()
28747 var label = this.el.select('label', true).first();
28748 var icon = this.el.select('i.fa-star', true).first();
28754 this.fireEvent('valid', this);
28757 getName: function()
28767 * http://masonry.desandro.com
28769 * The idea is to render all the bricks based on vertical width...
28771 * The original code extends 'outlayer' - we might need to use that....
28777 * @class Roo.bootstrap.LayoutMasonry
28778 * @extends Roo.bootstrap.Component
28779 * Bootstrap Layout Masonry class
28782 * Create a new Element
28783 * @param {Object} config The config object
28786 Roo.bootstrap.LayoutMasonry = function(config){
28787 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28793 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
28796 * @cfg {Boolean} isLayoutInstant = no animation?
28798 isLayoutInstant : false, // needed?
28801 * @cfg {Number} boxWidth width of the columns
28806 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
28811 * @cfg {Number} padWidth padding below box..
28816 * @cfg {Number} gutter gutter width..
28821 * @cfg {Number} maxCols maximum number of columns
28827 * @cfg {Boolean} isAutoInitial defalut true
28829 isAutoInitial : true,
28834 * @cfg {Boolean} isHorizontal defalut false
28836 isHorizontal : false,
28838 currentSize : null,
28844 bricks: null, //CompositeElement
28848 _isLayoutInited : false,
28850 // isAlternative : false, // only use for vertical layout...
28853 * @cfg {Number} alternativePadWidth padding below box..
28855 alternativePadWidth : 50,
28857 getAutoCreate : function(){
28861 cls: 'blog-masonary-wrapper ' + this.cls,
28863 cls : 'mas-boxes masonary'
28870 getChildContainer: function( )
28872 if (this.boxesEl) {
28873 return this.boxesEl;
28876 this.boxesEl = this.el.select('.mas-boxes').first();
28878 return this.boxesEl;
28882 initEvents : function()
28886 if(this.isAutoInitial){
28887 Roo.log('hook children rendered');
28888 this.on('childrenrendered', function() {
28889 Roo.log('children rendered');
28895 initial : function()
28897 this.currentSize = this.el.getBox(true);
28899 Roo.EventManager.onWindowResize(this.resize, this);
28901 if(!this.isAutoInitial){
28909 //this.layout.defer(500,this);
28913 resize : function()
28917 var cs = this.el.getBox(true);
28919 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28920 Roo.log("no change in with or X");
28924 this.currentSize = cs;
28930 layout : function()
28932 this._resetLayout();
28934 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28936 this.layoutItems( isInstant );
28938 this._isLayoutInited = true;
28942 _resetLayout : function()
28944 if(this.isHorizontal){
28945 this.horizontalMeasureColumns();
28949 this.verticalMeasureColumns();
28953 verticalMeasureColumns : function()
28955 this.getContainerWidth();
28957 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28958 // this.colWidth = Math.floor(this.containerWidth * 0.8);
28962 var boxWidth = this.boxWidth + this.padWidth;
28964 if(this.containerWidth < this.boxWidth){
28965 boxWidth = this.containerWidth
28968 var containerWidth = this.containerWidth;
28970 var cols = Math.floor(containerWidth / boxWidth);
28972 this.cols = Math.max( cols, 1 );
28974 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28976 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28978 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28980 this.colWidth = boxWidth + avail - this.padWidth;
28982 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28983 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
28986 horizontalMeasureColumns : function()
28988 this.getContainerWidth();
28990 var boxWidth = this.boxWidth;
28992 if(this.containerWidth < boxWidth){
28993 boxWidth = this.containerWidth;
28996 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28998 this.el.setHeight(boxWidth);
29002 getContainerWidth : function()
29004 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29007 layoutItems : function( isInstant )
29009 var items = Roo.apply([], this.bricks);
29011 if(this.isHorizontal){
29012 this._horizontalLayoutItems( items , isInstant );
29016 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29017 // this._verticalAlternativeLayoutItems( items , isInstant );
29021 this._verticalLayoutItems( items , isInstant );
29025 _verticalLayoutItems : function ( items , isInstant)
29027 if ( !items || !items.length ) {
29032 ['xs', 'xs', 'xs', 'tall'],
29033 ['xs', 'xs', 'tall'],
29034 ['xs', 'xs', 'sm'],
29035 ['xs', 'xs', 'xs'],
29041 ['sm', 'xs', 'xs'],
29045 ['tall', 'xs', 'xs', 'xs'],
29046 ['tall', 'xs', 'xs'],
29058 Roo.each(items, function(item, k){
29060 switch (item.size) {
29061 // these layouts take up a full box,
29072 boxes.push([item]);
29095 var filterPattern = function(box, length)
29103 var pattern = box.slice(0, length);
29107 Roo.each(pattern, function(i){
29108 format.push(i.size);
29111 Roo.each(standard, function(s){
29113 if(String(s) != String(format)){
29122 if(!match && length == 1){
29127 filterPattern(box, length - 1);
29131 queue.push(pattern);
29133 box = box.slice(length, box.length);
29135 filterPattern(box, 4);
29141 Roo.each(boxes, function(box, k){
29147 if(box.length == 1){
29152 filterPattern(box, 4);
29156 this._processVerticalLayoutQueue( queue, isInstant );
29160 // _verticalAlternativeLayoutItems : function( items , isInstant )
29162 // if ( !items || !items.length ) {
29166 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29170 _horizontalLayoutItems : function ( items , isInstant)
29172 if ( !items || !items.length || items.length < 3) {
29178 var eItems = items.slice(0, 3);
29180 items = items.slice(3, items.length);
29183 ['xs', 'xs', 'xs', 'wide'],
29184 ['xs', 'xs', 'wide'],
29185 ['xs', 'xs', 'sm'],
29186 ['xs', 'xs', 'xs'],
29192 ['sm', 'xs', 'xs'],
29196 ['wide', 'xs', 'xs', 'xs'],
29197 ['wide', 'xs', 'xs'],
29210 Roo.each(items, function(item, k){
29212 switch (item.size) {
29223 boxes.push([item]);
29247 var filterPattern = function(box, length)
29255 var pattern = box.slice(0, length);
29259 Roo.each(pattern, function(i){
29260 format.push(i.size);
29263 Roo.each(standard, function(s){
29265 if(String(s) != String(format)){
29274 if(!match && length == 1){
29279 filterPattern(box, length - 1);
29283 queue.push(pattern);
29285 box = box.slice(length, box.length);
29287 filterPattern(box, 4);
29293 Roo.each(boxes, function(box, k){
29299 if(box.length == 1){
29304 filterPattern(box, 4);
29311 var pos = this.el.getBox(true);
29315 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29317 var hit_end = false;
29319 Roo.each(queue, function(box){
29323 Roo.each(box, function(b){
29325 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29335 Roo.each(box, function(b){
29337 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29340 mx = Math.max(mx, b.x);
29344 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29348 Roo.each(box, function(b){
29350 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29364 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29367 /** Sets position of item in DOM
29368 * @param {Element} item
29369 * @param {Number} x - horizontal position
29370 * @param {Number} y - vertical position
29371 * @param {Boolean} isInstant - disables transitions
29373 _processVerticalLayoutQueue : function( queue, isInstant )
29375 var pos = this.el.getBox(true);
29380 for (var i = 0; i < this.cols; i++){
29384 Roo.each(queue, function(box, k){
29386 var col = k % this.cols;
29388 Roo.each(box, function(b,kk){
29390 b.el.position('absolute');
29392 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29393 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29395 if(b.size == 'md-left' || b.size == 'md-right'){
29396 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29397 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29400 b.el.setWidth(width);
29401 b.el.setHeight(height);
29403 b.el.select('iframe',true).setSize(width,height);
29407 for (var i = 0; i < this.cols; i++){
29409 if(maxY[i] < maxY[col]){
29414 col = Math.min(col, i);
29418 x = pos.x + col * (this.colWidth + this.padWidth);
29422 var positions = [];
29424 switch (box.length){
29426 positions = this.getVerticalOneBoxColPositions(x, y, box);
29429 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29432 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29435 positions = this.getVerticalFourBoxColPositions(x, y, box);
29441 Roo.each(box, function(b,kk){
29443 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29445 var sz = b.el.getSize();
29447 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29455 for (var i = 0; i < this.cols; i++){
29456 mY = Math.max(mY, maxY[i]);
29459 this.el.setHeight(mY - pos.y);
29463 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29465 // var pos = this.el.getBox(true);
29468 // var maxX = pos.right;
29470 // var maxHeight = 0;
29472 // Roo.each(items, function(item, k){
29476 // item.el.position('absolute');
29478 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29480 // item.el.setWidth(width);
29482 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29484 // item.el.setHeight(height);
29487 // item.el.setXY([x, y], isInstant ? false : true);
29489 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29492 // y = y + height + this.alternativePadWidth;
29494 // maxHeight = maxHeight + height + this.alternativePadWidth;
29498 // this.el.setHeight(maxHeight);
29502 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29504 var pos = this.el.getBox(true);
29509 var maxX = pos.right;
29511 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29513 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29515 Roo.each(queue, function(box, k){
29517 Roo.each(box, function(b, kk){
29519 b.el.position('absolute');
29521 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29522 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29524 if(b.size == 'md-left' || b.size == 'md-right'){
29525 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29526 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29529 b.el.setWidth(width);
29530 b.el.setHeight(height);
29538 var positions = [];
29540 switch (box.length){
29542 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29545 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29548 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29551 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29557 Roo.each(box, function(b,kk){
29559 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29561 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29569 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29571 Roo.each(eItems, function(b,k){
29573 b.size = (k == 0) ? 'sm' : 'xs';
29574 b.x = (k == 0) ? 2 : 1;
29575 b.y = (k == 0) ? 2 : 1;
29577 b.el.position('absolute');
29579 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29581 b.el.setWidth(width);
29583 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29585 b.el.setHeight(height);
29589 var positions = [];
29592 x : maxX - this.unitWidth * 2 - this.gutter,
29597 x : maxX - this.unitWidth,
29598 y : minY + (this.unitWidth + this.gutter) * 2
29602 x : maxX - this.unitWidth * 3 - this.gutter * 2,
29606 Roo.each(eItems, function(b,k){
29608 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29614 getVerticalOneBoxColPositions : function(x, y, box)
29618 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29620 if(box[0].size == 'md-left'){
29624 if(box[0].size == 'md-right'){
29629 x : x + (this.unitWidth + this.gutter) * rand,
29636 getVerticalTwoBoxColPositions : function(x, y, box)
29640 if(box[0].size == 'xs'){
29644 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29648 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29662 x : x + (this.unitWidth + this.gutter) * 2,
29663 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29670 getVerticalThreeBoxColPositions : function(x, y, box)
29674 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29682 x : x + (this.unitWidth + this.gutter) * 1,
29687 x : x + (this.unitWidth + this.gutter) * 2,
29695 if(box[0].size == 'xs' && box[1].size == 'xs'){
29704 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29708 x : x + (this.unitWidth + this.gutter) * 1,
29722 x : x + (this.unitWidth + this.gutter) * 2,
29727 x : x + (this.unitWidth + this.gutter) * 2,
29728 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29735 getVerticalFourBoxColPositions : function(x, y, box)
29739 if(box[0].size == 'xs'){
29748 y : y + (this.unitHeight + this.gutter) * 1
29753 y : y + (this.unitHeight + this.gutter) * 2
29757 x : x + (this.unitWidth + this.gutter) * 1,
29771 x : x + (this.unitWidth + this.gutter) * 2,
29776 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29777 y : y + (this.unitHeight + this.gutter) * 1
29781 x : x + (this.unitWidth + this.gutter) * 2,
29782 y : y + (this.unitWidth + this.gutter) * 2
29789 getHorizontalOneBoxColPositions : function(maxX, minY, box)
29793 if(box[0].size == 'md-left'){
29795 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29802 if(box[0].size == 'md-right'){
29804 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29805 y : minY + (this.unitWidth + this.gutter) * 1
29811 var rand = Math.floor(Math.random() * (4 - box[0].y));
29814 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29815 y : minY + (this.unitWidth + this.gutter) * rand
29822 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29826 if(box[0].size == 'xs'){
29829 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29834 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29835 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29843 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29848 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29849 y : minY + (this.unitWidth + this.gutter) * 2
29856 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29860 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29863 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29868 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29869 y : minY + (this.unitWidth + this.gutter) * 1
29873 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29874 y : minY + (this.unitWidth + this.gutter) * 2
29881 if(box[0].size == 'xs' && box[1].size == 'xs'){
29884 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29889 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29894 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29895 y : minY + (this.unitWidth + this.gutter) * 1
29903 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29908 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29909 y : minY + (this.unitWidth + this.gutter) * 2
29913 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29914 y : minY + (this.unitWidth + this.gutter) * 2
29921 getHorizontalFourBoxColPositions : function(maxX, minY, box)
29925 if(box[0].size == 'xs'){
29928 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29933 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29938 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),
29943 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29944 y : minY + (this.unitWidth + this.gutter) * 1
29952 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29957 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29958 y : minY + (this.unitWidth + this.gutter) * 2
29962 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29963 y : minY + (this.unitWidth + this.gutter) * 2
29967 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),
29968 y : minY + (this.unitWidth + this.gutter) * 2
29982 * http://masonry.desandro.com
29984 * The idea is to render all the bricks based on vertical width...
29986 * The original code extends 'outlayer' - we might need to use that....
29992 * @class Roo.bootstrap.LayoutMasonryAuto
29993 * @extends Roo.bootstrap.Component
29994 * Bootstrap Layout Masonry class
29997 * Create a new Element
29998 * @param {Object} config The config object
30001 Roo.bootstrap.LayoutMasonryAuto = function(config){
30002 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30005 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30008 * @cfg {Boolean} isFitWidth - resize the width..
30010 isFitWidth : false, // options..
30012 * @cfg {Boolean} isOriginLeft = left align?
30014 isOriginLeft : true,
30016 * @cfg {Boolean} isOriginTop = top align?
30018 isOriginTop : false,
30020 * @cfg {Boolean} isLayoutInstant = no animation?
30022 isLayoutInstant : false, // needed?
30024 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30026 isResizingContainer : true,
30028 * @cfg {Number} columnWidth width of the columns
30034 * @cfg {Number} maxCols maximum number of columns
30039 * @cfg {Number} padHeight padding below box..
30045 * @cfg {Boolean} isAutoInitial defalut true
30048 isAutoInitial : true,
30054 initialColumnWidth : 0,
30055 currentSize : null,
30057 colYs : null, // array.
30064 bricks: null, //CompositeElement
30065 cols : 0, // array?
30066 // element : null, // wrapped now this.el
30067 _isLayoutInited : null,
30070 getAutoCreate : function(){
30074 cls: 'blog-masonary-wrapper ' + this.cls,
30076 cls : 'mas-boxes masonary'
30083 getChildContainer: function( )
30085 if (this.boxesEl) {
30086 return this.boxesEl;
30089 this.boxesEl = this.el.select('.mas-boxes').first();
30091 return this.boxesEl;
30095 initEvents : function()
30099 if(this.isAutoInitial){
30100 Roo.log('hook children rendered');
30101 this.on('childrenrendered', function() {
30102 Roo.log('children rendered');
30109 initial : function()
30111 this.reloadItems();
30113 this.currentSize = this.el.getBox(true);
30115 /// was window resize... - let's see if this works..
30116 Roo.EventManager.onWindowResize(this.resize, this);
30118 if(!this.isAutoInitial){
30123 this.layout.defer(500,this);
30126 reloadItems: function()
30128 this.bricks = this.el.select('.masonry-brick', true);
30130 this.bricks.each(function(b) {
30131 //Roo.log(b.getSize());
30132 if (!b.attr('originalwidth')) {
30133 b.attr('originalwidth', b.getSize().width);
30138 Roo.log(this.bricks.elements.length);
30141 resize : function()
30144 var cs = this.el.getBox(true);
30146 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30147 Roo.log("no change in with or X");
30150 this.currentSize = cs;
30154 layout : function()
30157 this._resetLayout();
30158 //this._manageStamps();
30160 // don't animate first layout
30161 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30162 this.layoutItems( isInstant );
30164 // flag for initalized
30165 this._isLayoutInited = true;
30168 layoutItems : function( isInstant )
30170 //var items = this._getItemsForLayout( this.items );
30171 // original code supports filtering layout items.. we just ignore it..
30173 this._layoutItems( this.bricks , isInstant );
30175 this._postLayout();
30177 _layoutItems : function ( items , isInstant)
30179 //this.fireEvent( 'layout', this, items );
30182 if ( !items || !items.elements.length ) {
30183 // no items, emit event with empty array
30188 items.each(function(item) {
30189 Roo.log("layout item");
30191 // get x/y object from method
30192 var position = this._getItemLayoutPosition( item );
30194 position.item = item;
30195 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30196 queue.push( position );
30199 this._processLayoutQueue( queue );
30201 /** Sets position of item in DOM
30202 * @param {Element} item
30203 * @param {Number} x - horizontal position
30204 * @param {Number} y - vertical position
30205 * @param {Boolean} isInstant - disables transitions
30207 _processLayoutQueue : function( queue )
30209 for ( var i=0, len = queue.length; i < len; i++ ) {
30210 var obj = queue[i];
30211 obj.item.position('absolute');
30212 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30218 * Any logic you want to do after each layout,
30219 * i.e. size the container
30221 _postLayout : function()
30223 this.resizeContainer();
30226 resizeContainer : function()
30228 if ( !this.isResizingContainer ) {
30231 var size = this._getContainerSize();
30233 this.el.setSize(size.width,size.height);
30234 this.boxesEl.setSize(size.width,size.height);
30240 _resetLayout : function()
30242 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30243 this.colWidth = this.el.getWidth();
30244 //this.gutter = this.el.getWidth();
30246 this.measureColumns();
30252 this.colYs.push( 0 );
30258 measureColumns : function()
30260 this.getContainerWidth();
30261 // if columnWidth is 0, default to outerWidth of first item
30262 if ( !this.columnWidth ) {
30263 var firstItem = this.bricks.first();
30264 Roo.log(firstItem);
30265 this.columnWidth = this.containerWidth;
30266 if (firstItem && firstItem.attr('originalwidth') ) {
30267 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30269 // columnWidth fall back to item of first element
30270 Roo.log("set column width?");
30271 this.initialColumnWidth = this.columnWidth ;
30273 // if first elem has no width, default to size of container
30278 if (this.initialColumnWidth) {
30279 this.columnWidth = this.initialColumnWidth;
30284 // column width is fixed at the top - however if container width get's smaller we should
30287 // this bit calcs how man columns..
30289 var columnWidth = this.columnWidth += this.gutter;
30291 // calculate columns
30292 var containerWidth = this.containerWidth + this.gutter;
30294 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30295 // fix rounding errors, typically with gutters
30296 var excess = columnWidth - containerWidth % columnWidth;
30299 // if overshoot is less than a pixel, round up, otherwise floor it
30300 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30301 cols = Math[ mathMethod ]( cols );
30302 this.cols = Math.max( cols, 1 );
30303 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30305 // padding positioning..
30306 var totalColWidth = this.cols * this.columnWidth;
30307 var padavail = this.containerWidth - totalColWidth;
30308 // so for 2 columns - we need 3 'pads'
30310 var padNeeded = (1+this.cols) * this.padWidth;
30312 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30314 this.columnWidth += padExtra
30315 //this.padWidth = Math.floor(padavail / ( this.cols));
30317 // adjust colum width so that padding is fixed??
30319 // we have 3 columns ... total = width * 3
30320 // we have X left over... that should be used by
30322 //if (this.expandC) {
30330 getContainerWidth : function()
30332 /* // container is parent if fit width
30333 var container = this.isFitWidth ? this.element.parentNode : this.element;
30334 // check that this.size and size are there
30335 // IE8 triggers resize on body size change, so they might not be
30337 var size = getSize( container ); //FIXME
30338 this.containerWidth = size && size.innerWidth; //FIXME
30341 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30345 _getItemLayoutPosition : function( item ) // what is item?
30347 // we resize the item to our columnWidth..
30349 item.setWidth(this.columnWidth);
30350 item.autoBoxAdjust = false;
30352 var sz = item.getSize();
30354 // how many columns does this brick span
30355 var remainder = this.containerWidth % this.columnWidth;
30357 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30358 // round if off by 1 pixel, otherwise use ceil
30359 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30360 colSpan = Math.min( colSpan, this.cols );
30362 // normally this should be '1' as we dont' currently allow multi width columns..
30364 var colGroup = this._getColGroup( colSpan );
30365 // get the minimum Y value from the columns
30366 var minimumY = Math.min.apply( Math, colGroup );
30367 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30369 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30371 // position the brick
30373 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30374 y: this.currentSize.y + minimumY + this.padHeight
30378 // apply setHeight to necessary columns
30379 var setHeight = minimumY + sz.height + this.padHeight;
30380 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30382 var setSpan = this.cols + 1 - colGroup.length;
30383 for ( var i = 0; i < setSpan; i++ ) {
30384 this.colYs[ shortColIndex + i ] = setHeight ;
30391 * @param {Number} colSpan - number of columns the element spans
30392 * @returns {Array} colGroup
30394 _getColGroup : function( colSpan )
30396 if ( colSpan < 2 ) {
30397 // if brick spans only one column, use all the column Ys
30402 // how many different places could this brick fit horizontally
30403 var groupCount = this.cols + 1 - colSpan;
30404 // for each group potential horizontal position
30405 for ( var i = 0; i < groupCount; i++ ) {
30406 // make an array of colY values for that one group
30407 var groupColYs = this.colYs.slice( i, i + colSpan );
30408 // and get the max value of the array
30409 colGroup[i] = Math.max.apply( Math, groupColYs );
30414 _manageStamp : function( stamp )
30416 var stampSize = stamp.getSize();
30417 var offset = stamp.getBox();
30418 // get the columns that this stamp affects
30419 var firstX = this.isOriginLeft ? offset.x : offset.right;
30420 var lastX = firstX + stampSize.width;
30421 var firstCol = Math.floor( firstX / this.columnWidth );
30422 firstCol = Math.max( 0, firstCol );
30424 var lastCol = Math.floor( lastX / this.columnWidth );
30425 // lastCol should not go over if multiple of columnWidth #425
30426 lastCol -= lastX % this.columnWidth ? 0 : 1;
30427 lastCol = Math.min( this.cols - 1, lastCol );
30429 // set colYs to bottom of the stamp
30430 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30433 for ( var i = firstCol; i <= lastCol; i++ ) {
30434 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30439 _getContainerSize : function()
30441 this.maxY = Math.max.apply( Math, this.colYs );
30446 if ( this.isFitWidth ) {
30447 size.width = this._getContainerFitWidth();
30453 _getContainerFitWidth : function()
30455 var unusedCols = 0;
30456 // count unused columns
30459 if ( this.colYs[i] !== 0 ) {
30464 // fit container to columns that have been used
30465 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30468 needsResizeLayout : function()
30470 var previousWidth = this.containerWidth;
30471 this.getContainerWidth();
30472 return previousWidth !== this.containerWidth;
30487 * @class Roo.bootstrap.MasonryBrick
30488 * @extends Roo.bootstrap.Component
30489 * Bootstrap MasonryBrick class
30492 * Create a new MasonryBrick
30493 * @param {Object} config The config object
30496 Roo.bootstrap.MasonryBrick = function(config){
30497 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30503 * When a MasonryBrick is clcik
30504 * @param {Roo.bootstrap.MasonryBrick} this
30505 * @param {Roo.EventObject} e
30511 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30514 * @cfg {String} title
30518 * @cfg {String} html
30522 * @cfg {String} bgimage
30526 * @cfg {String} videourl
30530 * @cfg {String} cls
30534 * @cfg {String} href
30538 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30543 * @cfg {String} (center|bottom) placetitle
30547 getAutoCreate : function()
30549 var cls = 'masonry-brick';
30551 if(this.href.length){
30552 cls += ' masonry-brick-link';
30555 if(this.bgimage.length){
30556 cls += ' masonry-brick-image';
30560 cls += ' masonry-' + this.size + '-brick';
30563 if(this.placetitle.length){
30565 switch (this.placetitle) {
30567 cls += ' masonry-center-title';
30570 cls += ' masonry-bottom-title';
30577 if(!this.html.length && !this.bgimage.length){
30578 cls += ' masonry-center-title';
30581 if(!this.html.length && this.bgimage.length){
30582 cls += ' masonry-bottom-title';
30587 cls += ' ' + this.cls;
30591 tag: (this.href.length) ? 'a' : 'div',
30596 cls: 'masonry-brick-paragraph',
30602 if(this.href.length){
30603 cfg.href = this.href;
30606 var cn = cfg.cn[0].cn;
30608 if(this.title.length){
30611 cls: 'masonry-brick-title',
30616 if(this.html.length){
30619 cls: 'masonry-brick-text',
30623 if (!this.title.length && !this.html.length) {
30624 cfg.cn[0].cls += ' hide';
30627 if(this.bgimage.length){
30630 cls: 'masonry-brick-image-view',
30634 if(this.videourl.length){
30635 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30636 // youtube support only?
30639 cls: 'masonry-brick-image-view',
30642 allowfullscreen : true
30651 initEvents: function()
30653 switch (this.size) {
30655 // this.intSize = 1;
30660 // this.intSize = 2;
30667 // this.intSize = 3;
30672 // this.intSize = 3;
30677 // this.intSize = 3;
30682 // this.intSize = 3;
30694 this.el.on('touchstart', this.onTouchStart, this);
30695 this.el.on('touchmove', this.onTouchMove, this);
30696 this.el.on('touchend', this.onTouchEnd, this);
30697 this.el.on('contextmenu', this.onContextMenu, this);
30699 this.el.on('mouseenter' ,this.enter, this);
30700 this.el.on('mouseleave', this.leave, this);
30703 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30704 this.parent().bricks.push(this);
30709 onClick: function(e, el)
30715 var time = this.endTimer - this.startTimer;
30723 e.preventDefault();
30726 enter: function(e, el)
30728 e.preventDefault();
30730 if(this.bgimage.length && this.html.length){
30731 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30735 leave: function(e, el)
30737 e.preventDefault();
30739 if(this.bgimage.length && this.html.length){
30740 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30744 onTouchStart: function(e, el)
30746 // e.preventDefault();
30748 this.touchmoved = false;
30750 if(!this.bgimage.length || !this.html.length){
30754 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30756 this.timer = new Date().getTime();
30760 onTouchMove: function(e, el)
30762 this.touchmoved = true;
30765 onContextMenu : function(e,el)
30767 e.preventDefault();
30768 e.stopPropagation();
30772 onTouchEnd: function(e, el)
30774 // e.preventDefault();
30776 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30783 if(!this.bgimage.length || !this.html.length){
30785 if(this.href.length){
30786 window.location.href = this.href;
30792 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30794 window.location.href = this.href;
30809 * @class Roo.bootstrap.Brick
30810 * @extends Roo.bootstrap.Component
30811 * Bootstrap Brick class
30814 * Create a new Brick
30815 * @param {Object} config The config object
30818 Roo.bootstrap.Brick = function(config){
30819 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30825 * When a Brick is click
30826 * @param {Roo.bootstrap.Brick} this
30827 * @param {Roo.EventObject} e
30833 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
30836 * @cfg {String} title
30840 * @cfg {String} html
30844 * @cfg {String} bgimage
30848 * @cfg {String} cls
30852 * @cfg {String} href
30856 * @cfg {String} video
30860 * @cfg {Boolean} square
30864 getAutoCreate : function()
30866 var cls = 'roo-brick';
30868 if(this.href.length){
30869 cls += ' roo-brick-link';
30872 if(this.bgimage.length){
30873 cls += ' roo-brick-image';
30876 if(!this.html.length && !this.bgimage.length){
30877 cls += ' roo-brick-center-title';
30880 if(!this.html.length && this.bgimage.length){
30881 cls += ' roo-brick-bottom-title';
30885 cls += ' ' + this.cls;
30889 tag: (this.href.length) ? 'a' : 'div',
30894 cls: 'roo-brick-paragraph',
30900 if(this.href.length){
30901 cfg.href = this.href;
30904 var cn = cfg.cn[0].cn;
30906 if(this.title.length){
30909 cls: 'roo-brick-title',
30914 if(this.html.length){
30917 cls: 'roo-brick-text',
30924 if(this.bgimage.length){
30927 cls: 'roo-brick-image-view',
30935 initEvents: function()
30937 if(this.title.length || this.html.length){
30938 this.el.on('mouseenter' ,this.enter, this);
30939 this.el.on('mouseleave', this.leave, this);
30943 Roo.EventManager.onWindowResize(this.resize, this);
30948 resize : function()
30950 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30952 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30953 // paragraph.setHeight(paragraph.getWidth());
30955 if(this.bgimage.length){
30956 var image = this.el.select('.roo-brick-image-view', true).first();
30957 image.setWidth(paragraph.getWidth());
30958 image.setHeight(paragraph.getWidth());
30963 enter: function(e, el)
30965 e.preventDefault();
30967 if(this.bgimage.length){
30968 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30969 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30973 leave: function(e, el)
30975 e.preventDefault();
30977 if(this.bgimage.length){
30978 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30979 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30989 * Ext JS Library 1.1.1
30990 * Copyright(c) 2006-2007, Ext JS, LLC.
30992 * Originally Released Under LGPL - original licence link has changed is not relivant.
30995 * <script type="text/javascript">
31000 * @class Roo.bootstrap.SplitBar
31001 * @extends Roo.util.Observable
31002 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31006 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31007 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31008 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31009 split.minSize = 100;
31010 split.maxSize = 600;
31011 split.animate = true;
31012 split.on('moved', splitterMoved);
31015 * Create a new SplitBar
31016 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
31017 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
31018 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31019 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
31020 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31021 position of the SplitBar).
31023 Roo.bootstrap.SplitBar = function(cfg){
31028 // dragElement : elm
31029 // resizingElement: el,
31031 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31032 // placement : Roo.bootstrap.SplitBar.LEFT ,
31033 // existingProxy ???
31036 this.el = Roo.get(cfg.dragElement, true);
31037 this.el.dom.unselectable = "on";
31039 this.resizingEl = Roo.get(cfg.resizingElement, true);
31043 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31044 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31047 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31050 * The minimum size of the resizing element. (Defaults to 0)
31056 * The maximum size of the resizing element. (Defaults to 2000)
31059 this.maxSize = 2000;
31062 * Whether to animate the transition to the new size
31065 this.animate = false;
31068 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31071 this.useShim = false;
31076 if(!cfg.existingProxy){
31078 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31080 this.proxy = Roo.get(cfg.existingProxy).dom;
31083 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31086 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31089 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31092 this.dragSpecs = {};
31095 * @private The adapter to use to positon and resize elements
31097 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31098 this.adapter.init(this);
31100 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31102 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31103 this.el.addClass("roo-splitbar-h");
31106 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31107 this.el.addClass("roo-splitbar-v");
31113 * Fires when the splitter is moved (alias for {@link #event-moved})
31114 * @param {Roo.bootstrap.SplitBar} this
31115 * @param {Number} newSize the new width or height
31120 * Fires when the splitter is moved
31121 * @param {Roo.bootstrap.SplitBar} this
31122 * @param {Number} newSize the new width or height
31126 * @event beforeresize
31127 * Fires before the splitter is dragged
31128 * @param {Roo.bootstrap.SplitBar} this
31130 "beforeresize" : true,
31132 "beforeapply" : true
31135 Roo.util.Observable.call(this);
31138 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31139 onStartProxyDrag : function(x, y){
31140 this.fireEvent("beforeresize", this);
31142 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
31144 o.enableDisplayMode("block");
31145 // all splitbars share the same overlay
31146 Roo.bootstrap.SplitBar.prototype.overlay = o;
31148 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31149 this.overlay.show();
31150 Roo.get(this.proxy).setDisplayed("block");
31151 var size = this.adapter.getElementSize(this);
31152 this.activeMinSize = this.getMinimumSize();;
31153 this.activeMaxSize = this.getMaximumSize();;
31154 var c1 = size - this.activeMinSize;
31155 var c2 = Math.max(this.activeMaxSize - size, 0);
31156 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31157 this.dd.resetConstraints();
31158 this.dd.setXConstraint(
31159 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
31160 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31162 this.dd.setYConstraint(0, 0);
31164 this.dd.resetConstraints();
31165 this.dd.setXConstraint(0, 0);
31166 this.dd.setYConstraint(
31167 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
31168 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31171 this.dragSpecs.startSize = size;
31172 this.dragSpecs.startPoint = [x, y];
31173 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31177 * @private Called after the drag operation by the DDProxy
31179 onEndProxyDrag : function(e){
31180 Roo.get(this.proxy).setDisplayed(false);
31181 var endPoint = Roo.lib.Event.getXY(e);
31183 this.overlay.hide();
31186 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31187 newSize = this.dragSpecs.startSize +
31188 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31189 endPoint[0] - this.dragSpecs.startPoint[0] :
31190 this.dragSpecs.startPoint[0] - endPoint[0]
31193 newSize = this.dragSpecs.startSize +
31194 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31195 endPoint[1] - this.dragSpecs.startPoint[1] :
31196 this.dragSpecs.startPoint[1] - endPoint[1]
31199 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31200 if(newSize != this.dragSpecs.startSize){
31201 if(this.fireEvent('beforeapply', this, newSize) !== false){
31202 this.adapter.setElementSize(this, newSize);
31203 this.fireEvent("moved", this, newSize);
31204 this.fireEvent("resize", this, newSize);
31210 * Get the adapter this SplitBar uses
31211 * @return The adapter object
31213 getAdapter : function(){
31214 return this.adapter;
31218 * Set the adapter this SplitBar uses
31219 * @param {Object} adapter A SplitBar adapter object
31221 setAdapter : function(adapter){
31222 this.adapter = adapter;
31223 this.adapter.init(this);
31227 * Gets the minimum size for the resizing element
31228 * @return {Number} The minimum size
31230 getMinimumSize : function(){
31231 return this.minSize;
31235 * Sets the minimum size for the resizing element
31236 * @param {Number} minSize The minimum size
31238 setMinimumSize : function(minSize){
31239 this.minSize = minSize;
31243 * Gets the maximum size for the resizing element
31244 * @return {Number} The maximum size
31246 getMaximumSize : function(){
31247 return this.maxSize;
31251 * Sets the maximum size for the resizing element
31252 * @param {Number} maxSize The maximum size
31254 setMaximumSize : function(maxSize){
31255 this.maxSize = maxSize;
31259 * Sets the initialize size for the resizing element
31260 * @param {Number} size The initial size
31262 setCurrentSize : function(size){
31263 var oldAnimate = this.animate;
31264 this.animate = false;
31265 this.adapter.setElementSize(this, size);
31266 this.animate = oldAnimate;
31270 * Destroy this splitbar.
31271 * @param {Boolean} removeEl True to remove the element
31273 destroy : function(removeEl){
31275 this.shim.remove();
31278 this.proxy.parentNode.removeChild(this.proxy);
31286 * @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.
31288 Roo.bootstrap.SplitBar.createProxy = function(dir){
31289 var proxy = new Roo.Element(document.createElement("div"));
31290 proxy.unselectable();
31291 var cls = 'roo-splitbar-proxy';
31292 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31293 document.body.appendChild(proxy.dom);
31298 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31299 * Default Adapter. It assumes the splitter and resizing element are not positioned
31300 * elements and only gets/sets the width of the element. Generally used for table based layouts.
31302 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31305 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31306 // do nothing for now
31307 init : function(s){
31311 * Called before drag operations to get the current size of the resizing element.
31312 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31314 getElementSize : function(s){
31315 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31316 return s.resizingEl.getWidth();
31318 return s.resizingEl.getHeight();
31323 * Called after drag operations to set the size of the resizing element.
31324 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31325 * @param {Number} newSize The new size to set
31326 * @param {Function} onComplete A function to be invoked when resizing is complete
31328 setElementSize : function(s, newSize, onComplete){
31329 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31331 s.resizingEl.setWidth(newSize);
31333 onComplete(s, newSize);
31336 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31341 s.resizingEl.setHeight(newSize);
31343 onComplete(s, newSize);
31346 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31353 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31354 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31355 * Adapter that moves the splitter element to align with the resized sizing element.
31356 * Used with an absolute positioned SplitBar.
31357 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31358 * document.body, make sure you assign an id to the body element.
31360 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31361 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31362 this.container = Roo.get(container);
31365 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31366 init : function(s){
31367 this.basic.init(s);
31370 getElementSize : function(s){
31371 return this.basic.getElementSize(s);
31374 setElementSize : function(s, newSize, onComplete){
31375 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31378 moveSplitter : function(s){
31379 var yes = Roo.bootstrap.SplitBar;
31380 switch(s.placement){
31382 s.el.setX(s.resizingEl.getRight());
31385 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31388 s.el.setY(s.resizingEl.getBottom());
31391 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31398 * Orientation constant - Create a vertical SplitBar
31402 Roo.bootstrap.SplitBar.VERTICAL = 1;
31405 * Orientation constant - Create a horizontal SplitBar
31409 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31412 * Placement constant - The resizing element is to the left of the splitter element
31416 Roo.bootstrap.SplitBar.LEFT = 1;
31419 * Placement constant - The resizing element is to the right of the splitter element
31423 Roo.bootstrap.SplitBar.RIGHT = 2;
31426 * Placement constant - The resizing element is positioned above the splitter element
31430 Roo.bootstrap.SplitBar.TOP = 3;
31433 * Placement constant - The resizing element is positioned under splitter element
31437 Roo.bootstrap.SplitBar.BOTTOM = 4;
31438 Roo.namespace("Roo.bootstrap.layout");/*
31440 * Ext JS Library 1.1.1
31441 * Copyright(c) 2006-2007, Ext JS, LLC.
31443 * Originally Released Under LGPL - original licence link has changed is not relivant.
31446 * <script type="text/javascript">
31450 * @class Roo.bootstrap.layout.Manager
31451 * @extends Roo.bootstrap.Component
31452 * Base class for layout managers.
31454 Roo.bootstrap.layout.Manager = function(config)
31456 Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31462 /** false to disable window resize monitoring @type Boolean */
31463 this.monitorWindowResize = true;
31468 * Fires when a layout is performed.
31469 * @param {Roo.LayoutManager} this
31473 * @event regionresized
31474 * Fires when the user resizes a region.
31475 * @param {Roo.LayoutRegion} region The resized region
31476 * @param {Number} newSize The new size (width for east/west, height for north/south)
31478 "regionresized" : true,
31480 * @event regioncollapsed
31481 * Fires when a region is collapsed.
31482 * @param {Roo.LayoutRegion} region The collapsed region
31484 "regioncollapsed" : true,
31486 * @event regionexpanded
31487 * Fires when a region is expanded.
31488 * @param {Roo.LayoutRegion} region The expanded region
31490 "regionexpanded" : true
31492 this.updating = false;
31495 this.el = Roo.get(config.el);
31501 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31506 monitorWindowResize : true,
31512 onRender : function(ct, position)
31515 this.el = Roo.get(ct);
31521 initEvents: function()
31525 // ie scrollbar fix
31526 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31527 document.body.scroll = "no";
31528 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31529 this.el.position('relative');
31531 this.id = this.el.id;
31532 this.el.addClass("roo-layout-container");
31533 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31534 if(this.el.dom != document.body ) {
31535 this.el.on('resize', this.layout,this);
31536 this.el.on('show', this.layout,this);
31542 * Returns true if this layout is currently being updated
31543 * @return {Boolean}
31545 isUpdating : function(){
31546 return this.updating;
31550 * Suspend the LayoutManager from doing auto-layouts while
31551 * making multiple add or remove calls
31553 beginUpdate : function(){
31554 this.updating = true;
31558 * Restore auto-layouts and optionally disable the manager from performing a layout
31559 * @param {Boolean} noLayout true to disable a layout update
31561 endUpdate : function(noLayout){
31562 this.updating = false;
31568 layout: function(){
31572 onRegionResized : function(region, newSize){
31573 this.fireEvent("regionresized", region, newSize);
31577 onRegionCollapsed : function(region){
31578 this.fireEvent("regioncollapsed", region);
31581 onRegionExpanded : function(region){
31582 this.fireEvent("regionexpanded", region);
31586 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31587 * performs box-model adjustments.
31588 * @return {Object} The size as an object {width: (the width), height: (the height)}
31590 getViewSize : function()
31593 if(this.el.dom != document.body){
31594 size = this.el.getSize();
31596 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31598 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31599 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31604 * Returns the Element this layout is bound to.
31605 * @return {Roo.Element}
31607 getEl : function(){
31612 * Returns the specified region.
31613 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31614 * @return {Roo.LayoutRegion}
31616 getRegion : function(target){
31617 return this.regions[target.toLowerCase()];
31620 onWindowResize : function(){
31621 if(this.monitorWindowResize){
31627 * Ext JS Library 1.1.1
31628 * Copyright(c) 2006-2007, Ext JS, LLC.
31630 * Originally Released Under LGPL - original licence link has changed is not relivant.
31633 * <script type="text/javascript">
31636 * @class Roo.bootstrap.layout.Border
31637 * @extends Roo.bootstrap.layout.Manager
31638 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31639 * please see: examples/bootstrap/nested.html<br><br>
31641 <b>The container the layout is rendered into can be either the body element or any other element.
31642 If it is not the body element, the container needs to either be an absolute positioned element,
31643 or you will need to add "position:relative" to the css of the container. You will also need to specify
31644 the container size if it is not the body element.</b>
31647 * Create a new Border
31648 * @param {Object} config Configuration options
31650 Roo.bootstrap.layout.Border = function(config){
31651 config = config || {};
31652 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31656 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31657 if(config[region]){
31658 config[region].region = region;
31659 this.addRegion(config[region]);
31665 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
31667 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31669 * Creates and adds a new region if it doesn't already exist.
31670 * @param {String} target The target region key (north, south, east, west or center).
31671 * @param {Object} config The regions config object
31672 * @return {BorderLayoutRegion} The new region
31674 addRegion : function(config)
31676 if(!this.regions[config.region]){
31677 var r = this.factory(config);
31678 this.bindRegion(r);
31680 return this.regions[config.region];
31684 bindRegion : function(r){
31685 this.regions[r.config.region] = r;
31687 r.on("visibilitychange", this.layout, this);
31688 r.on("paneladded", this.layout, this);
31689 r.on("panelremoved", this.layout, this);
31690 r.on("invalidated", this.layout, this);
31691 r.on("resized", this.onRegionResized, this);
31692 r.on("collapsed", this.onRegionCollapsed, this);
31693 r.on("expanded", this.onRegionExpanded, this);
31697 * Performs a layout update.
31699 layout : function()
31701 if(this.updating) {
31704 var size = this.getViewSize();
31705 var w = size.width;
31706 var h = size.height;
31711 //var x = 0, y = 0;
31713 var rs = this.regions;
31714 var north = rs["north"];
31715 var south = rs["south"];
31716 var west = rs["west"];
31717 var east = rs["east"];
31718 var center = rs["center"];
31719 //if(this.hideOnLayout){ // not supported anymore
31720 //c.el.setStyle("display", "none");
31722 if(north && north.isVisible()){
31723 var b = north.getBox();
31724 var m = north.getMargins();
31725 b.width = w - (m.left+m.right);
31728 centerY = b.height + b.y + m.bottom;
31729 centerH -= centerY;
31730 north.updateBox(this.safeBox(b));
31732 if(south && south.isVisible()){
31733 var b = south.getBox();
31734 var m = south.getMargins();
31735 b.width = w - (m.left+m.right);
31737 var totalHeight = (b.height + m.top + m.bottom);
31738 b.y = h - totalHeight + m.top;
31739 centerH -= totalHeight;
31740 south.updateBox(this.safeBox(b));
31742 if(west && west.isVisible()){
31743 var b = west.getBox();
31744 var m = west.getMargins();
31745 b.height = centerH - (m.top+m.bottom);
31747 b.y = centerY + m.top;
31748 var totalWidth = (b.width + m.left + m.right);
31749 centerX += totalWidth;
31750 centerW -= totalWidth;
31751 west.updateBox(this.safeBox(b));
31753 if(east && east.isVisible()){
31754 var b = east.getBox();
31755 var m = east.getMargins();
31756 b.height = centerH - (m.top+m.bottom);
31757 var totalWidth = (b.width + m.left + m.right);
31758 b.x = w - totalWidth + m.left;
31759 b.y = centerY + m.top;
31760 centerW -= totalWidth;
31761 east.updateBox(this.safeBox(b));
31764 var m = center.getMargins();
31766 x: centerX + m.left,
31767 y: centerY + m.top,
31768 width: centerW - (m.left+m.right),
31769 height: centerH - (m.top+m.bottom)
31771 //if(this.hideOnLayout){
31772 //center.el.setStyle("display", "block");
31774 center.updateBox(this.safeBox(centerBox));
31777 this.fireEvent("layout", this);
31781 safeBox : function(box){
31782 box.width = Math.max(0, box.width);
31783 box.height = Math.max(0, box.height);
31788 * Adds a ContentPanel (or subclass) to this layout.
31789 * @param {String} target The target region key (north, south, east, west or center).
31790 * @param {Roo.ContentPanel} panel The panel to add
31791 * @return {Roo.ContentPanel} The added panel
31793 add : function(target, panel){
31795 target = target.toLowerCase();
31796 return this.regions[target].add(panel);
31800 * Remove a ContentPanel (or subclass) to this layout.
31801 * @param {String} target The target region key (north, south, east, west or center).
31802 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31803 * @return {Roo.ContentPanel} The removed panel
31805 remove : function(target, panel){
31806 target = target.toLowerCase();
31807 return this.regions[target].remove(panel);
31811 * Searches all regions for a panel with the specified id
31812 * @param {String} panelId
31813 * @return {Roo.ContentPanel} The panel or null if it wasn't found
31815 findPanel : function(panelId){
31816 var rs = this.regions;
31817 for(var target in rs){
31818 if(typeof rs[target] != "function"){
31819 var p = rs[target].getPanel(panelId);
31829 * Searches all regions for a panel with the specified id and activates (shows) it.
31830 * @param {String/ContentPanel} panelId The panels id or the panel itself
31831 * @return {Roo.ContentPanel} The shown panel or null
31833 showPanel : function(panelId) {
31834 var rs = this.regions;
31835 for(var target in rs){
31836 var r = rs[target];
31837 if(typeof r != "function"){
31838 if(r.hasPanel(panelId)){
31839 return r.showPanel(panelId);
31847 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31848 * @param {Roo.state.Provider} provider (optional) An alternate state provider
31851 restoreState : function(provider){
31853 provider = Roo.state.Manager;
31855 var sm = new Roo.LayoutStateManager();
31856 sm.init(this, provider);
31862 * Adds a xtype elements to the layout.
31866 xtype : 'ContentPanel',
31873 xtype : 'NestedLayoutPanel',
31879 items : [ ... list of content panels or nested layout panels.. ]
31883 * @param {Object} cfg Xtype definition of item to add.
31885 addxtype : function(cfg)
31887 // basically accepts a pannel...
31888 // can accept a layout region..!?!?
31889 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31892 // theory? children can only be panels??
31894 //if (!cfg.xtype.match(/Panel$/)) {
31899 if (typeof(cfg.region) == 'undefined') {
31900 Roo.log("Failed to add Panel, region was not set");
31904 var region = cfg.region;
31910 xitems = cfg.items;
31917 case 'Content': // ContentPanel (el, cfg)
31918 case 'Scroll': // ContentPanel (el, cfg)
31920 cfg.autoCreate = true;
31921 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31923 // var el = this.el.createChild();
31924 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31927 this.add(region, ret);
31931 case 'TreePanel': // our new panel!
31932 cfg.el = this.el.createChild();
31933 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31934 this.add(region, ret);
31939 // create a new Layout (which is a Border Layout...
31941 var clayout = cfg.layout;
31942 clayout.el = this.el.createChild();
31943 clayout.items = clayout.items || [];
31947 // replace this exitems with the clayout ones..
31948 xitems = clayout.items;
31950 // force background off if it's in center...
31951 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31952 cfg.background = false;
31954 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
31957 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31958 //console.log('adding nested layout panel ' + cfg.toSource());
31959 this.add(region, ret);
31960 nb = {}; /// find first...
31965 // needs grid and region
31967 //var el = this.getRegion(region).el.createChild();
31969 *var el = this.el.createChild();
31970 // create the grid first...
31971 cfg.grid.container = el;
31972 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31975 if (region == 'center' && this.active ) {
31976 cfg.background = false;
31979 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31981 this.add(region, ret);
31983 if (cfg.background) {
31984 // render grid on panel activation (if panel background)
31985 ret.on('activate', function(gp) {
31986 if (!gp.grid.rendered) {
31987 // gp.grid.render(el);
31991 // cfg.grid.render(el);
31997 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31998 // it was the old xcomponent building that caused this before.
31999 // espeically if border is the top element in the tree.
32009 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32011 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32012 this.add(region, ret);
32016 throw "Can not add '" + cfg.xtype + "' to Border";
32022 this.beginUpdate();
32026 Roo.each(xitems, function(i) {
32027 region = nb && i.region ? i.region : false;
32029 var add = ret.addxtype(i);
32032 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32033 if (!i.background) {
32034 abn[region] = nb[region] ;
32041 // make the last non-background panel active..
32042 //if (nb) { Roo.log(abn); }
32045 for(var r in abn) {
32046 region = this.getRegion(r);
32048 // tried using nb[r], but it does not work..
32050 region.showPanel(abn[r]);
32061 factory : function(cfg)
32064 var validRegions = Roo.bootstrap.layout.Border.regions;
32066 var target = cfg.region;
32069 var r = Roo.bootstrap.layout;
32073 return new r.North(cfg);
32075 return new r.South(cfg);
32077 return new r.East(cfg);
32079 return new r.West(cfg);
32081 return new r.Center(cfg);
32083 throw 'Layout region "'+target+'" not supported.';
32090 * Ext JS Library 1.1.1
32091 * Copyright(c) 2006-2007, Ext JS, LLC.
32093 * Originally Released Under LGPL - original licence link has changed is not relivant.
32096 * <script type="text/javascript">
32100 * @class Roo.bootstrap.layout.Basic
32101 * @extends Roo.util.Observable
32102 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32103 * and does not have a titlebar, tabs or any other features. All it does is size and position
32104 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32105 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32106 * @cfg {string} region the region that it inhabits..
32107 * @cfg {bool} skipConfig skip config?
32111 Roo.bootstrap.layout.Basic = function(config){
32113 this.mgr = config.mgr;
32115 this.position = config.region;
32117 var skipConfig = config.skipConfig;
32121 * @scope Roo.BasicLayoutRegion
32125 * @event beforeremove
32126 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32127 * @param {Roo.LayoutRegion} this
32128 * @param {Roo.ContentPanel} panel The panel
32129 * @param {Object} e The cancel event object
32131 "beforeremove" : true,
32133 * @event invalidated
32134 * Fires when the layout for this region is changed.
32135 * @param {Roo.LayoutRegion} this
32137 "invalidated" : true,
32139 * @event visibilitychange
32140 * Fires when this region is shown or hidden
32141 * @param {Roo.LayoutRegion} this
32142 * @param {Boolean} visibility true or false
32144 "visibilitychange" : true,
32146 * @event paneladded
32147 * Fires when a panel is added.
32148 * @param {Roo.LayoutRegion} this
32149 * @param {Roo.ContentPanel} panel The panel
32151 "paneladded" : true,
32153 * @event panelremoved
32154 * Fires when a panel is removed.
32155 * @param {Roo.LayoutRegion} this
32156 * @param {Roo.ContentPanel} panel The panel
32158 "panelremoved" : true,
32160 * @event beforecollapse
32161 * Fires when this region before collapse.
32162 * @param {Roo.LayoutRegion} this
32164 "beforecollapse" : true,
32167 * Fires when this region is collapsed.
32168 * @param {Roo.LayoutRegion} this
32170 "collapsed" : true,
32173 * Fires when this region is expanded.
32174 * @param {Roo.LayoutRegion} this
32179 * Fires when this region is slid into view.
32180 * @param {Roo.LayoutRegion} this
32182 "slideshow" : true,
32185 * Fires when this region slides out of view.
32186 * @param {Roo.LayoutRegion} this
32188 "slidehide" : true,
32190 * @event panelactivated
32191 * Fires when a panel is activated.
32192 * @param {Roo.LayoutRegion} this
32193 * @param {Roo.ContentPanel} panel The activated panel
32195 "panelactivated" : true,
32198 * Fires when the user resizes this region.
32199 * @param {Roo.LayoutRegion} this
32200 * @param {Number} newSize The new size (width for east/west, height for north/south)
32204 /** A collection of panels in this region. @type Roo.util.MixedCollection */
32205 this.panels = new Roo.util.MixedCollection();
32206 this.panels.getKey = this.getPanelId.createDelegate(this);
32208 this.activePanel = null;
32209 // ensure listeners are added...
32211 if (config.listeners || config.events) {
32212 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32213 listeners : config.listeners || {},
32214 events : config.events || {}
32218 if(skipConfig !== true){
32219 this.applyConfig(config);
32223 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32225 getPanelId : function(p){
32229 applyConfig : function(config){
32230 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32231 this.config = config;
32236 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
32237 * the width, for horizontal (north, south) the height.
32238 * @param {Number} newSize The new width or height
32240 resizeTo : function(newSize){
32241 var el = this.el ? this.el :
32242 (this.activePanel ? this.activePanel.getEl() : null);
32244 switch(this.position){
32247 el.setWidth(newSize);
32248 this.fireEvent("resized", this, newSize);
32252 el.setHeight(newSize);
32253 this.fireEvent("resized", this, newSize);
32259 getBox : function(){
32260 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32263 getMargins : function(){
32264 return this.margins;
32267 updateBox : function(box){
32269 var el = this.activePanel.getEl();
32270 el.dom.style.left = box.x + "px";
32271 el.dom.style.top = box.y + "px";
32272 this.activePanel.setSize(box.width, box.height);
32276 * Returns the container element for this region.
32277 * @return {Roo.Element}
32279 getEl : function(){
32280 return this.activePanel;
32284 * Returns true if this region is currently visible.
32285 * @return {Boolean}
32287 isVisible : function(){
32288 return this.activePanel ? true : false;
32291 setActivePanel : function(panel){
32292 panel = this.getPanel(panel);
32293 if(this.activePanel && this.activePanel != panel){
32294 this.activePanel.setActiveState(false);
32295 this.activePanel.getEl().setLeftTop(-10000,-10000);
32297 this.activePanel = panel;
32298 panel.setActiveState(true);
32300 panel.setSize(this.box.width, this.box.height);
32302 this.fireEvent("panelactivated", this, panel);
32303 this.fireEvent("invalidated");
32307 * Show the specified panel.
32308 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32309 * @return {Roo.ContentPanel} The shown panel or null
32311 showPanel : function(panel){
32312 panel = this.getPanel(panel);
32314 this.setActivePanel(panel);
32320 * Get the active panel for this region.
32321 * @return {Roo.ContentPanel} The active panel or null
32323 getActivePanel : function(){
32324 return this.activePanel;
32328 * Add the passed ContentPanel(s)
32329 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32330 * @return {Roo.ContentPanel} The panel added (if only one was added)
32332 add : function(panel){
32333 if(arguments.length > 1){
32334 for(var i = 0, len = arguments.length; i < len; i++) {
32335 this.add(arguments[i]);
32339 if(this.hasPanel(panel)){
32340 this.showPanel(panel);
32343 var el = panel.getEl();
32344 if(el.dom.parentNode != this.mgr.el.dom){
32345 this.mgr.el.dom.appendChild(el.dom);
32347 if(panel.setRegion){
32348 panel.setRegion(this);
32350 this.panels.add(panel);
32351 el.setStyle("position", "absolute");
32352 if(!panel.background){
32353 this.setActivePanel(panel);
32354 if(this.config.initialSize && this.panels.getCount()==1){
32355 this.resizeTo(this.config.initialSize);
32358 this.fireEvent("paneladded", this, panel);
32363 * Returns true if the panel is in this region.
32364 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32365 * @return {Boolean}
32367 hasPanel : function(panel){
32368 if(typeof panel == "object"){ // must be panel obj
32369 panel = panel.getId();
32371 return this.getPanel(panel) ? true : false;
32375 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32376 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32377 * @param {Boolean} preservePanel Overrides the config preservePanel option
32378 * @return {Roo.ContentPanel} The panel that was removed
32380 remove : function(panel, preservePanel){
32381 panel = this.getPanel(panel);
32386 this.fireEvent("beforeremove", this, panel, e);
32387 if(e.cancel === true){
32390 var panelId = panel.getId();
32391 this.panels.removeKey(panelId);
32396 * Returns the panel specified or null if it's not in this region.
32397 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32398 * @return {Roo.ContentPanel}
32400 getPanel : function(id){
32401 if(typeof id == "object"){ // must be panel obj
32404 return this.panels.get(id);
32408 * Returns this regions position (north/south/east/west/center).
32411 getPosition: function(){
32412 return this.position;
32416 * Ext JS Library 1.1.1
32417 * Copyright(c) 2006-2007, Ext JS, LLC.
32419 * Originally Released Under LGPL - original licence link has changed is not relivant.
32422 * <script type="text/javascript">
32426 * @class Roo.bootstrap.layout.Region
32427 * @extends Roo.bootstrap.layout.Basic
32428 * This class represents a region in a layout manager.
32430 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32431 * @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})
32432 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
32433 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
32434 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
32435 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
32436 * @cfg {String} title The title for the region (overrides panel titles)
32437 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
32438 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32439 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
32440 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32441 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
32442 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32443 * the space available, similar to FireFox 1.5 tabs (defaults to false)
32444 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
32445 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
32446 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
32448 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
32449 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
32450 * @cfg {Boolean} disableTabTips True to disable tab tooltips
32451 * @cfg {Number} width For East/West panels
32452 * @cfg {Number} height For North/South panels
32453 * @cfg {Boolean} split To show the splitter
32454 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
32456 * @cfg {string} cls Extra CSS classes to add to region
32458 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32459 * @cfg {string} region the region that it inhabits..
32462 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
32463 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
32465 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
32466 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
32467 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
32469 Roo.bootstrap.layout.Region = function(config)
32471 this.applyConfig(config);
32473 var mgr = config.mgr;
32474 var pos = config.region;
32475 config.skipConfig = true;
32476 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32479 this.onRender(mgr.el);
32482 this.visible = true;
32483 this.collapsed = false;
32486 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32488 position: '', // set by wrapper (eg. north/south etc..)
32490 createBody : function(){
32491 /** This region's body element
32492 * @type Roo.Element */
32493 this.bodyEl = this.el.createChild({
32495 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32499 onRender: function(ctr, pos)
32501 var dh = Roo.DomHelper;
32502 /** This region's container element
32503 * @type Roo.Element */
32504 this.el = dh.append(ctr.dom, {
32506 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32508 /** This region's title element
32509 * @type Roo.Element */
32511 this.titleEl = dh.append(this.el.dom,
32514 unselectable: "on",
32515 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32517 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
32518 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32521 this.titleEl.enableDisplayMode();
32522 /** This region's title text element
32523 * @type HTMLElement */
32524 this.titleTextEl = this.titleEl.dom.firstChild;
32525 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32527 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32528 this.closeBtn.enableDisplayMode();
32529 this.closeBtn.on("click", this.closeClicked, this);
32530 this.closeBtn.hide();
32532 this.createBody(this.config);
32533 if(this.config.hideWhenEmpty){
32535 this.on("paneladded", this.validateVisibility, this);
32536 this.on("panelremoved", this.validateVisibility, this);
32538 if(this.autoScroll){
32539 this.bodyEl.setStyle("overflow", "auto");
32541 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32543 //if(c.titlebar !== false){
32544 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32545 this.titleEl.hide();
32547 this.titleEl.show();
32548 if(this.config.title){
32549 this.titleTextEl.innerHTML = this.config.title;
32553 if(this.config.collapsed){
32554 this.collapse(true);
32556 if(this.config.hidden){
32561 applyConfig : function(c)
32564 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32565 var dh = Roo.DomHelper;
32566 if(c.titlebar !== false){
32567 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32568 this.collapseBtn.on("click", this.collapse, this);
32569 this.collapseBtn.enableDisplayMode();
32571 if(c.showPin === true || this.showPin){
32572 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32573 this.stickBtn.enableDisplayMode();
32574 this.stickBtn.on("click", this.expand, this);
32575 this.stickBtn.hide();
32580 /** This region's collapsed element
32581 * @type Roo.Element */
32584 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32585 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32588 if(c.floatable !== false){
32589 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32590 this.collapsedEl.on("click", this.collapseClick, this);
32593 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32594 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32595 id: "message", unselectable: "on", style:{"float":"left"}});
32596 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32598 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32599 this.expandBtn.on("click", this.expand, this);
32603 if(this.collapseBtn){
32604 this.collapseBtn.setVisible(c.collapsible == true);
32607 this.cmargins = c.cmargins || this.cmargins ||
32608 (this.position == "west" || this.position == "east" ?
32609 {top: 0, left: 2, right:2, bottom: 0} :
32610 {top: 2, left: 0, right:0, bottom: 2});
32612 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32615 this.bottomTabs = c.tabPosition != "top";
32617 this.autoScroll = c.autoScroll || false;
32622 this.duration = c.duration || .30;
32623 this.slideDuration = c.slideDuration || .45;
32628 * Returns true if this region is currently visible.
32629 * @return {Boolean}
32631 isVisible : function(){
32632 return this.visible;
32636 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32637 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
32639 //setCollapsedTitle : function(title){
32640 // title = title || " ";
32641 // if(this.collapsedTitleTextEl){
32642 // this.collapsedTitleTextEl.innerHTML = title;
32646 getBox : function(){
32648 // if(!this.collapsed){
32649 b = this.el.getBox(false, true);
32651 // b = this.collapsedEl.getBox(false, true);
32656 getMargins : function(){
32657 return this.margins;
32658 //return this.collapsed ? this.cmargins : this.margins;
32661 highlight : function(){
32662 this.el.addClass("x-layout-panel-dragover");
32665 unhighlight : function(){
32666 this.el.removeClass("x-layout-panel-dragover");
32669 updateBox : function(box)
32672 if(!this.collapsed){
32673 this.el.dom.style.left = box.x + "px";
32674 this.el.dom.style.top = box.y + "px";
32675 this.updateBody(box.width, box.height);
32677 this.collapsedEl.dom.style.left = box.x + "px";
32678 this.collapsedEl.dom.style.top = box.y + "px";
32679 this.collapsedEl.setSize(box.width, box.height);
32682 this.tabs.autoSizeTabs();
32686 updateBody : function(w, h)
32689 this.el.setWidth(w);
32690 w -= this.el.getBorderWidth("rl");
32691 if(this.config.adjustments){
32692 w += this.config.adjustments[0];
32696 this.el.setHeight(h);
32697 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32698 h -= this.el.getBorderWidth("tb");
32699 if(this.config.adjustments){
32700 h += this.config.adjustments[1];
32702 this.bodyEl.setHeight(h);
32704 h = this.tabs.syncHeight(h);
32707 if(this.panelSize){
32708 w = w !== null ? w : this.panelSize.width;
32709 h = h !== null ? h : this.panelSize.height;
32711 if(this.activePanel){
32712 var el = this.activePanel.getEl();
32713 w = w !== null ? w : el.getWidth();
32714 h = h !== null ? h : el.getHeight();
32715 this.panelSize = {width: w, height: h};
32716 this.activePanel.setSize(w, h);
32718 if(Roo.isIE && this.tabs){
32719 this.tabs.el.repaint();
32724 * Returns the container element for this region.
32725 * @return {Roo.Element}
32727 getEl : function(){
32732 * Hides this region.
32735 //if(!this.collapsed){
32736 this.el.dom.style.left = "-2000px";
32739 // this.collapsedEl.dom.style.left = "-2000px";
32740 // this.collapsedEl.hide();
32742 this.visible = false;
32743 this.fireEvent("visibilitychange", this, false);
32747 * Shows this region if it was previously hidden.
32750 //if(!this.collapsed){
32753 // this.collapsedEl.show();
32755 this.visible = true;
32756 this.fireEvent("visibilitychange", this, true);
32759 closeClicked : function(){
32760 if(this.activePanel){
32761 this.remove(this.activePanel);
32765 collapseClick : function(e){
32767 e.stopPropagation();
32770 e.stopPropagation();
32776 * Collapses this region.
32777 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32780 collapse : function(skipAnim, skipCheck = false){
32781 if(this.collapsed) {
32785 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32787 this.collapsed = true;
32789 this.split.el.hide();
32791 if(this.config.animate && skipAnim !== true){
32792 this.fireEvent("invalidated", this);
32793 this.animateCollapse();
32795 this.el.setLocation(-20000,-20000);
32797 this.collapsedEl.show();
32798 this.fireEvent("collapsed", this);
32799 this.fireEvent("invalidated", this);
32805 animateCollapse : function(){
32810 * Expands this region if it was previously collapsed.
32811 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32812 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32815 expand : function(e, skipAnim){
32817 e.stopPropagation();
32819 if(!this.collapsed || this.el.hasActiveFx()) {
32823 this.afterSlideIn();
32826 this.collapsed = false;
32827 if(this.config.animate && skipAnim !== true){
32828 this.animateExpand();
32832 this.split.el.show();
32834 this.collapsedEl.setLocation(-2000,-2000);
32835 this.collapsedEl.hide();
32836 this.fireEvent("invalidated", this);
32837 this.fireEvent("expanded", this);
32841 animateExpand : function(){
32845 initTabs : function()
32847 this.bodyEl.setStyle("overflow", "hidden");
32848 var ts = new Roo.bootstrap.panel.Tabs({
32849 el: this.bodyEl.dom,
32850 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32851 disableTooltips: this.config.disableTabTips,
32852 toolbar : this.config.toolbar
32855 if(this.config.hideTabs){
32856 ts.stripWrap.setDisplayed(false);
32859 ts.resizeTabs = this.config.resizeTabs === true;
32860 ts.minTabWidth = this.config.minTabWidth || 40;
32861 ts.maxTabWidth = this.config.maxTabWidth || 250;
32862 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32863 ts.monitorResize = false;
32864 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32865 ts.bodyEl.addClass('roo-layout-tabs-body');
32866 this.panels.each(this.initPanelAsTab, this);
32869 initPanelAsTab : function(panel){
32870 var ti = this.tabs.addTab(
32872 panel.getTitle(), null,
32873 this.config.closeOnTab && panel.isClosable()
32875 if(panel.tabTip !== undefined){
32876 ti.setTooltip(panel.tabTip);
32878 ti.on("activate", function(){
32879 this.setActivePanel(panel);
32882 if(this.config.closeOnTab){
32883 ti.on("beforeclose", function(t, e){
32885 this.remove(panel);
32891 updatePanelTitle : function(panel, title)
32893 if(this.activePanel == panel){
32894 this.updateTitle(title);
32897 var ti = this.tabs.getTab(panel.getEl().id);
32899 if(panel.tabTip !== undefined){
32900 ti.setTooltip(panel.tabTip);
32905 updateTitle : function(title){
32906 if(this.titleTextEl && !this.config.title){
32907 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
32911 setActivePanel : function(panel)
32913 panel = this.getPanel(panel);
32914 if(this.activePanel && this.activePanel != panel){
32915 this.activePanel.setActiveState(false);
32917 this.activePanel = panel;
32918 panel.setActiveState(true);
32919 if(this.panelSize){
32920 panel.setSize(this.panelSize.width, this.panelSize.height);
32923 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32925 this.updateTitle(panel.getTitle());
32927 this.fireEvent("invalidated", this);
32929 this.fireEvent("panelactivated", this, panel);
32933 * Shows the specified panel.
32934 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32935 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32937 showPanel : function(panel)
32939 panel = this.getPanel(panel);
32942 var tab = this.tabs.getTab(panel.getEl().id);
32943 if(tab.isHidden()){
32944 this.tabs.unhideTab(tab.id);
32948 this.setActivePanel(panel);
32955 * Get the active panel for this region.
32956 * @return {Roo.ContentPanel} The active panel or null
32958 getActivePanel : function(){
32959 return this.activePanel;
32962 validateVisibility : function(){
32963 if(this.panels.getCount() < 1){
32964 this.updateTitle(" ");
32965 this.closeBtn.hide();
32968 if(!this.isVisible()){
32975 * Adds the passed ContentPanel(s) to this region.
32976 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32977 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32979 add : function(panel){
32980 if(arguments.length > 1){
32981 for(var i = 0, len = arguments.length; i < len; i++) {
32982 this.add(arguments[i]);
32986 if(this.hasPanel(panel)){
32987 this.showPanel(panel);
32990 panel.setRegion(this);
32991 this.panels.add(panel);
32992 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32993 this.bodyEl.dom.appendChild(panel.getEl().dom);
32994 if(panel.background !== true){
32995 this.setActivePanel(panel);
32997 this.fireEvent("paneladded", this, panel);
33003 this.initPanelAsTab(panel);
33007 if(panel.background !== true){
33008 this.tabs.activate(panel.getEl().id);
33010 this.fireEvent("paneladded", this, panel);
33015 * Hides the tab for the specified panel.
33016 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33018 hidePanel : function(panel){
33019 if(this.tabs && (panel = this.getPanel(panel))){
33020 this.tabs.hideTab(panel.getEl().id);
33025 * Unhides the tab for a previously hidden panel.
33026 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33028 unhidePanel : function(panel){
33029 if(this.tabs && (panel = this.getPanel(panel))){
33030 this.tabs.unhideTab(panel.getEl().id);
33034 clearPanels : function(){
33035 while(this.panels.getCount() > 0){
33036 this.remove(this.panels.first());
33041 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33042 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33043 * @param {Boolean} preservePanel Overrides the config preservePanel option
33044 * @return {Roo.ContentPanel} The panel that was removed
33046 remove : function(panel, preservePanel)
33048 panel = this.getPanel(panel);
33053 this.fireEvent("beforeremove", this, panel, e);
33054 if(e.cancel === true){
33057 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33058 var panelId = panel.getId();
33059 this.panels.removeKey(panelId);
33061 document.body.appendChild(panel.getEl().dom);
33064 this.tabs.removeTab(panel.getEl().id);
33065 }else if (!preservePanel){
33066 this.bodyEl.dom.removeChild(panel.getEl().dom);
33068 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33069 var p = this.panels.first();
33070 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33071 tempEl.appendChild(p.getEl().dom);
33072 this.bodyEl.update("");
33073 this.bodyEl.dom.appendChild(p.getEl().dom);
33075 this.updateTitle(p.getTitle());
33077 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33078 this.setActivePanel(p);
33080 panel.setRegion(null);
33081 if(this.activePanel == panel){
33082 this.activePanel = null;
33084 if(this.config.autoDestroy !== false && preservePanel !== true){
33085 try{panel.destroy();}catch(e){}
33087 this.fireEvent("panelremoved", this, panel);
33092 * Returns the TabPanel component used by this region
33093 * @return {Roo.TabPanel}
33095 getTabs : function(){
33099 createTool : function(parentEl, className){
33100 var btn = Roo.DomHelper.append(parentEl, {
33102 cls: "x-layout-tools-button",
33105 cls: "roo-layout-tools-button-inner " + className,
33109 btn.addClassOnOver("roo-layout-tools-button-over");
33114 * Ext JS Library 1.1.1
33115 * Copyright(c) 2006-2007, Ext JS, LLC.
33117 * Originally Released Under LGPL - original licence link has changed is not relivant.
33120 * <script type="text/javascript">
33126 * @class Roo.SplitLayoutRegion
33127 * @extends Roo.LayoutRegion
33128 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33130 Roo.bootstrap.layout.Split = function(config){
33131 this.cursor = config.cursor;
33132 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33135 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33137 splitTip : "Drag to resize.",
33138 collapsibleSplitTip : "Drag to resize. Double click to hide.",
33139 useSplitTips : false,
33141 applyConfig : function(config){
33142 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33145 onRender : function(ctr,pos) {
33147 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33148 if(!this.config.split){
33153 var splitEl = Roo.DomHelper.append(ctr.dom, {
33155 id: this.el.id + "-split",
33156 cls: "roo-layout-split roo-layout-split-"+this.position,
33159 /** The SplitBar for this region
33160 * @type Roo.SplitBar */
33161 // does not exist yet...
33162 Roo.log([this.position, this.orientation]);
33164 this.split = new Roo.bootstrap.SplitBar({
33165 dragElement : splitEl,
33166 resizingElement: this.el,
33167 orientation : this.orientation
33170 this.split.on("moved", this.onSplitMove, this);
33171 this.split.useShim = this.config.useShim === true;
33172 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33173 if(this.useSplitTips){
33174 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33176 //if(config.collapsible){
33177 // this.split.el.on("dblclick", this.collapse, this);
33180 if(typeof this.config.minSize != "undefined"){
33181 this.split.minSize = this.config.minSize;
33183 if(typeof this.config.maxSize != "undefined"){
33184 this.split.maxSize = this.config.maxSize;
33186 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33187 this.hideSplitter();
33192 getHMaxSize : function(){
33193 var cmax = this.config.maxSize || 10000;
33194 var center = this.mgr.getRegion("center");
33195 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33198 getVMaxSize : function(){
33199 var cmax = this.config.maxSize || 10000;
33200 var center = this.mgr.getRegion("center");
33201 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33204 onSplitMove : function(split, newSize){
33205 this.fireEvent("resized", this, newSize);
33209 * Returns the {@link Roo.SplitBar} for this region.
33210 * @return {Roo.SplitBar}
33212 getSplitBar : function(){
33217 this.hideSplitter();
33218 Roo.bootstrap.layout.Split.superclass.hide.call(this);
33221 hideSplitter : function(){
33223 this.split.el.setLocation(-2000,-2000);
33224 this.split.el.hide();
33230 this.split.el.show();
33232 Roo.bootstrap.layout.Split.superclass.show.call(this);
33235 beforeSlide: function(){
33236 if(Roo.isGecko){// firefox overflow auto bug workaround
33237 this.bodyEl.clip();
33239 this.tabs.bodyEl.clip();
33241 if(this.activePanel){
33242 this.activePanel.getEl().clip();
33244 if(this.activePanel.beforeSlide){
33245 this.activePanel.beforeSlide();
33251 afterSlide : function(){
33252 if(Roo.isGecko){// firefox overflow auto bug workaround
33253 this.bodyEl.unclip();
33255 this.tabs.bodyEl.unclip();
33257 if(this.activePanel){
33258 this.activePanel.getEl().unclip();
33259 if(this.activePanel.afterSlide){
33260 this.activePanel.afterSlide();
33266 initAutoHide : function(){
33267 if(this.autoHide !== false){
33268 if(!this.autoHideHd){
33269 var st = new Roo.util.DelayedTask(this.slideIn, this);
33270 this.autoHideHd = {
33271 "mouseout": function(e){
33272 if(!e.within(this.el, true)){
33276 "mouseover" : function(e){
33282 this.el.on(this.autoHideHd);
33286 clearAutoHide : function(){
33287 if(this.autoHide !== false){
33288 this.el.un("mouseout", this.autoHideHd.mouseout);
33289 this.el.un("mouseover", this.autoHideHd.mouseover);
33293 clearMonitor : function(){
33294 Roo.get(document).un("click", this.slideInIf, this);
33297 // these names are backwards but not changed for compat
33298 slideOut : function(){
33299 if(this.isSlid || this.el.hasActiveFx()){
33302 this.isSlid = true;
33303 if(this.collapseBtn){
33304 this.collapseBtn.hide();
33306 this.closeBtnState = this.closeBtn.getStyle('display');
33307 this.closeBtn.hide();
33309 this.stickBtn.show();
33312 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33313 this.beforeSlide();
33314 this.el.setStyle("z-index", 10001);
33315 this.el.slideIn(this.getSlideAnchor(), {
33316 callback: function(){
33318 this.initAutoHide();
33319 Roo.get(document).on("click", this.slideInIf, this);
33320 this.fireEvent("slideshow", this);
33327 afterSlideIn : function(){
33328 this.clearAutoHide();
33329 this.isSlid = false;
33330 this.clearMonitor();
33331 this.el.setStyle("z-index", "");
33332 if(this.collapseBtn){
33333 this.collapseBtn.show();
33335 this.closeBtn.setStyle('display', this.closeBtnState);
33337 this.stickBtn.hide();
33339 this.fireEvent("slidehide", this);
33342 slideIn : function(cb){
33343 if(!this.isSlid || this.el.hasActiveFx()){
33347 this.isSlid = false;
33348 this.beforeSlide();
33349 this.el.slideOut(this.getSlideAnchor(), {
33350 callback: function(){
33351 this.el.setLeftTop(-10000, -10000);
33353 this.afterSlideIn();
33361 slideInIf : function(e){
33362 if(!e.within(this.el)){
33367 animateCollapse : function(){
33368 this.beforeSlide();
33369 this.el.setStyle("z-index", 20000);
33370 var anchor = this.getSlideAnchor();
33371 this.el.slideOut(anchor, {
33372 callback : function(){
33373 this.el.setStyle("z-index", "");
33374 this.collapsedEl.slideIn(anchor, {duration:.3});
33376 this.el.setLocation(-10000,-10000);
33378 this.fireEvent("collapsed", this);
33385 animateExpand : function(){
33386 this.beforeSlide();
33387 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33388 this.el.setStyle("z-index", 20000);
33389 this.collapsedEl.hide({
33392 this.el.slideIn(this.getSlideAnchor(), {
33393 callback : function(){
33394 this.el.setStyle("z-index", "");
33397 this.split.el.show();
33399 this.fireEvent("invalidated", this);
33400 this.fireEvent("expanded", this);
33428 getAnchor : function(){
33429 return this.anchors[this.position];
33432 getCollapseAnchor : function(){
33433 return this.canchors[this.position];
33436 getSlideAnchor : function(){
33437 return this.sanchors[this.position];
33440 getAlignAdj : function(){
33441 var cm = this.cmargins;
33442 switch(this.position){
33458 getExpandAdj : function(){
33459 var c = this.collapsedEl, cm = this.cmargins;
33460 switch(this.position){
33462 return [-(cm.right+c.getWidth()+cm.left), 0];
33465 return [cm.right+c.getWidth()+cm.left, 0];
33468 return [0, -(cm.top+cm.bottom+c.getHeight())];
33471 return [0, cm.top+cm.bottom+c.getHeight()];
33477 * Ext JS Library 1.1.1
33478 * Copyright(c) 2006-2007, Ext JS, LLC.
33480 * Originally Released Under LGPL - original licence link has changed is not relivant.
33483 * <script type="text/javascript">
33486 * These classes are private internal classes
33488 Roo.bootstrap.layout.Center = function(config){
33489 config.region = "center";
33490 Roo.bootstrap.layout.Region.call(this, config);
33491 this.visible = true;
33492 this.minWidth = config.minWidth || 20;
33493 this.minHeight = config.minHeight || 20;
33496 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33498 // center panel can't be hidden
33502 // center panel can't be hidden
33505 getMinWidth: function(){
33506 return this.minWidth;
33509 getMinHeight: function(){
33510 return this.minHeight;
33523 Roo.bootstrap.layout.North = function(config)
33525 config.region = 'north';
33526 config.cursor = 'n-resize';
33528 Roo.bootstrap.layout.Split.call(this, config);
33532 this.split.placement = Roo.bootstrap.SplitBar.TOP;
33533 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33534 this.split.el.addClass("roo-layout-split-v");
33536 var size = config.initialSize || config.height;
33537 if(typeof size != "undefined"){
33538 this.el.setHeight(size);
33541 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33543 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33547 getBox : function(){
33548 if(this.collapsed){
33549 return this.collapsedEl.getBox();
33551 var box = this.el.getBox();
33553 box.height += this.split.el.getHeight();
33558 updateBox : function(box){
33559 if(this.split && !this.collapsed){
33560 box.height -= this.split.el.getHeight();
33561 this.split.el.setLeft(box.x);
33562 this.split.el.setTop(box.y+box.height);
33563 this.split.el.setWidth(box.width);
33565 if(this.collapsed){
33566 this.updateBody(box.width, null);
33568 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33576 Roo.bootstrap.layout.South = function(config){
33577 config.region = 'south';
33578 config.cursor = 's-resize';
33579 Roo.bootstrap.layout.Split.call(this, config);
33581 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33582 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33583 this.split.el.addClass("roo-layout-split-v");
33585 var size = config.initialSize || config.height;
33586 if(typeof size != "undefined"){
33587 this.el.setHeight(size);
33591 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33592 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33593 getBox : function(){
33594 if(this.collapsed){
33595 return this.collapsedEl.getBox();
33597 var box = this.el.getBox();
33599 var sh = this.split.el.getHeight();
33606 updateBox : function(box){
33607 if(this.split && !this.collapsed){
33608 var sh = this.split.el.getHeight();
33611 this.split.el.setLeft(box.x);
33612 this.split.el.setTop(box.y-sh);
33613 this.split.el.setWidth(box.width);
33615 if(this.collapsed){
33616 this.updateBody(box.width, null);
33618 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33622 Roo.bootstrap.layout.East = function(config){
33623 config.region = "east";
33624 config.cursor = "e-resize";
33625 Roo.bootstrap.layout.Split.call(this, config);
33627 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33628 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33629 this.split.el.addClass("roo-layout-split-h");
33631 var size = config.initialSize || config.width;
33632 if(typeof size != "undefined"){
33633 this.el.setWidth(size);
33636 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33637 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33638 getBox : function(){
33639 if(this.collapsed){
33640 return this.collapsedEl.getBox();
33642 var box = this.el.getBox();
33644 var sw = this.split.el.getWidth();
33651 updateBox : function(box){
33652 if(this.split && !this.collapsed){
33653 var sw = this.split.el.getWidth();
33655 this.split.el.setLeft(box.x);
33656 this.split.el.setTop(box.y);
33657 this.split.el.setHeight(box.height);
33660 if(this.collapsed){
33661 this.updateBody(null, box.height);
33663 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33667 Roo.bootstrap.layout.West = function(config){
33668 config.region = "west";
33669 config.cursor = "w-resize";
33671 Roo.bootstrap.layout.Split.call(this, config);
33673 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33674 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33675 this.split.el.addClass("roo-layout-split-h");
33679 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33680 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33682 onRender: function(ctr, pos)
33684 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33685 var size = this.config.initialSize || this.config.width;
33686 if(typeof size != "undefined"){
33687 this.el.setWidth(size);
33691 getBox : function(){
33692 if(this.collapsed){
33693 return this.collapsedEl.getBox();
33695 var box = this.el.getBox();
33697 box.width += this.split.el.getWidth();
33702 updateBox : function(box){
33703 if(this.split && !this.collapsed){
33704 var sw = this.split.el.getWidth();
33706 this.split.el.setLeft(box.x+box.width);
33707 this.split.el.setTop(box.y);
33708 this.split.el.setHeight(box.height);
33710 if(this.collapsed){
33711 this.updateBody(null, box.height);
33713 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33716 Roo.namespace("Roo.bootstrap.panel");/*
33718 * Ext JS Library 1.1.1
33719 * Copyright(c) 2006-2007, Ext JS, LLC.
33721 * Originally Released Under LGPL - original licence link has changed is not relivant.
33724 * <script type="text/javascript">
33727 * @class Roo.ContentPanel
33728 * @extends Roo.util.Observable
33729 * A basic ContentPanel element.
33730 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
33731 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
33732 * @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
33733 * @cfg {Boolean} closable True if the panel can be closed/removed
33734 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
33735 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33736 * @cfg {Toolbar} toolbar A toolbar for this panel
33737 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
33738 * @cfg {String} title The title for this panel
33739 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33740 * @cfg {String} url Calls {@link #setUrl} with this value
33741 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33742 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
33743 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
33744 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
33747 * Create a new ContentPanel.
33748 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33749 * @param {String/Object} config A string to set only the title or a config object
33750 * @param {String} content (optional) Set the HTML content for this panel
33751 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33753 Roo.bootstrap.panel.Content = function( config){
33755 var el = config.el;
33756 var content = config.content;
33758 if(config.autoCreate){ // xtype is available if this is called from factory
33761 this.el = Roo.get(el);
33762 if(!this.el && config && config.autoCreate){
33763 if(typeof config.autoCreate == "object"){
33764 if(!config.autoCreate.id){
33765 config.autoCreate.id = config.id||el;
33767 this.el = Roo.DomHelper.append(document.body,
33768 config.autoCreate, true);
33770 var elcfg = { tag: "div",
33771 cls: "roo-layout-inactive-content",
33775 elcfg.html = config.html;
33779 this.el = Roo.DomHelper.append(document.body, elcfg , true);
33782 this.closable = false;
33783 this.loaded = false;
33784 this.active = false;
33787 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33789 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33791 this.wrapEl = this.el.wrap();
33793 if (config.toolbar.items) {
33794 ti = config.toolbar.items ;
33795 delete config.toolbar.items ;
33799 this.toolbar.render(this.wrapEl, 'before');
33800 for(var i =0;i < ti.length;i++) {
33801 // Roo.log(['add child', items[i]]);
33802 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33804 this.toolbar.items = nitems;
33805 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33806 delete config.toolbar;
33810 // xtype created footer. - not sure if will work as we normally have to render first..
33811 if (this.footer && !this.footer.el && this.footer.xtype) {
33812 if (!this.wrapEl) {
33813 this.wrapEl = this.el.wrap();
33816 this.footer.container = this.wrapEl.createChild();
33818 this.footer = Roo.factory(this.footer, Roo);
33823 if(typeof config == "string"){
33824 this.title = config;
33826 Roo.apply(this, config);
33830 this.resizeEl = Roo.get(this.resizeEl, true);
33832 this.resizeEl = this.el;
33834 // handle view.xtype
33842 * Fires when this panel is activated.
33843 * @param {Roo.ContentPanel} this
33847 * @event deactivate
33848 * Fires when this panel is activated.
33849 * @param {Roo.ContentPanel} this
33851 "deactivate" : true,
33855 * Fires when this panel is resized if fitToFrame is true.
33856 * @param {Roo.ContentPanel} this
33857 * @param {Number} width The width after any component adjustments
33858 * @param {Number} height The height after any component adjustments
33864 * Fires when this tab is created
33865 * @param {Roo.ContentPanel} this
33876 if(this.autoScroll){
33877 this.resizeEl.setStyle("overflow", "auto");
33879 // fix randome scrolling
33880 //this.el.on('scroll', function() {
33881 // Roo.log('fix random scolling');
33882 // this.scrollTo('top',0);
33885 content = content || this.content;
33887 this.setContent(content);
33889 if(config && config.url){
33890 this.setUrl(this.url, this.params, this.loadOnce);
33895 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33897 if (this.view && typeof(this.view.xtype) != 'undefined') {
33898 this.view.el = this.el.appendChild(document.createElement("div"));
33899 this.view = Roo.factory(this.view);
33900 this.view.render && this.view.render(false, '');
33904 this.fireEvent('render', this);
33907 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33909 setRegion : function(region){
33910 this.region = region;
33912 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33914 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33919 * Returns the toolbar for this Panel if one was configured.
33920 * @return {Roo.Toolbar}
33922 getToolbar : function(){
33923 return this.toolbar;
33926 setActiveState : function(active){
33927 this.active = active;
33929 this.fireEvent("deactivate", this);
33931 this.fireEvent("activate", this);
33935 * Updates this panel's element
33936 * @param {String} content The new content
33937 * @param {Boolean} loadScripts (optional) true to look for and process scripts
33939 setContent : function(content, loadScripts){
33940 this.el.update(content, loadScripts);
33943 ignoreResize : function(w, h){
33944 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33947 this.lastSize = {width: w, height: h};
33952 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33953 * @return {Roo.UpdateManager} The UpdateManager
33955 getUpdateManager : function(){
33956 return this.el.getUpdateManager();
33959 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33960 * @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:
33963 url: "your-url.php",
33964 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33965 callback: yourFunction,
33966 scope: yourObject, //(optional scope)
33969 text: "Loading...",
33974 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33975 * 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.
33976 * @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}
33977 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33978 * @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.
33979 * @return {Roo.ContentPanel} this
33982 var um = this.el.getUpdateManager();
33983 um.update.apply(um, arguments);
33989 * 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.
33990 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33991 * @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)
33992 * @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)
33993 * @return {Roo.UpdateManager} The UpdateManager
33995 setUrl : function(url, params, loadOnce){
33996 if(this.refreshDelegate){
33997 this.removeListener("activate", this.refreshDelegate);
33999 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34000 this.on("activate", this.refreshDelegate);
34001 return this.el.getUpdateManager();
34004 _handleRefresh : function(url, params, loadOnce){
34005 if(!loadOnce || !this.loaded){
34006 var updater = this.el.getUpdateManager();
34007 updater.update(url, params, this._setLoaded.createDelegate(this));
34011 _setLoaded : function(){
34012 this.loaded = true;
34016 * Returns this panel's id
34019 getId : function(){
34024 * Returns this panel's element - used by regiosn to add.
34025 * @return {Roo.Element}
34027 getEl : function(){
34028 return this.wrapEl || this.el;
34033 adjustForComponents : function(width, height)
34035 //Roo.log('adjustForComponents ');
34036 if(this.resizeEl != this.el){
34037 width -= this.el.getFrameWidth('lr');
34038 height -= this.el.getFrameWidth('tb');
34041 var te = this.toolbar.getEl();
34042 height -= te.getHeight();
34043 te.setWidth(width);
34046 var te = this.footer.getEl();
34047 Roo.log("footer:" + te.getHeight());
34049 height -= te.getHeight();
34050 te.setWidth(width);
34054 if(this.adjustments){
34055 width += this.adjustments[0];
34056 height += this.adjustments[1];
34058 return {"width": width, "height": height};
34061 setSize : function(width, height){
34062 if(this.fitToFrame && !this.ignoreResize(width, height)){
34063 if(this.fitContainer && this.resizeEl != this.el){
34064 this.el.setSize(width, height);
34066 var size = this.adjustForComponents(width, height);
34067 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34068 this.fireEvent('resize', this, size.width, size.height);
34073 * Returns this panel's title
34076 getTitle : function(){
34081 * Set this panel's title
34082 * @param {String} title
34084 setTitle : function(title){
34085 this.title = title;
34087 this.region.updatePanelTitle(this, title);
34092 * Returns true is this panel was configured to be closable
34093 * @return {Boolean}
34095 isClosable : function(){
34096 return this.closable;
34099 beforeSlide : function(){
34101 this.resizeEl.clip();
34104 afterSlide : function(){
34106 this.resizeEl.unclip();
34110 * Force a content refresh from the URL specified in the {@link #setUrl} method.
34111 * Will fail silently if the {@link #setUrl} method has not been called.
34112 * This does not activate the panel, just updates its content.
34114 refresh : function(){
34115 if(this.refreshDelegate){
34116 this.loaded = false;
34117 this.refreshDelegate();
34122 * Destroys this panel
34124 destroy : function(){
34125 this.el.removeAllListeners();
34126 var tempEl = document.createElement("span");
34127 tempEl.appendChild(this.el.dom);
34128 tempEl.innerHTML = "";
34134 * form - if the content panel contains a form - this is a reference to it.
34135 * @type {Roo.form.Form}
34139 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34140 * This contains a reference to it.
34146 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34156 * @param {Object} cfg Xtype definition of item to add.
34160 getChildContainer: function () {
34161 return this.getEl();
34166 var ret = new Roo.factory(cfg);
34171 if (cfg.xtype.match(/^Form$/)) {
34174 //if (this.footer) {
34175 // el = this.footer.container.insertSibling(false, 'before');
34177 el = this.el.createChild();
34180 this.form = new Roo.form.Form(cfg);
34183 if ( this.form.allItems.length) {
34184 this.form.render(el.dom);
34188 // should only have one of theses..
34189 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34190 // views.. should not be just added - used named prop 'view''
34192 cfg.el = this.el.appendChild(document.createElement("div"));
34195 var ret = new Roo.factory(cfg);
34197 ret.render && ret.render(false, ''); // render blank..
34207 * @class Roo.bootstrap.panel.Grid
34208 * @extends Roo.bootstrap.panel.Content
34210 * Create a new GridPanel.
34211 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34212 * @param {Object} config A the config object
34218 Roo.bootstrap.panel.Grid = function(config)
34222 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34223 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34225 config.el = this.wrapper;
34226 //this.el = this.wrapper;
34228 if (config.container) {
34229 // ctor'ed from a Border/panel.grid
34232 this.wrapper.setStyle("overflow", "hidden");
34233 this.wrapper.addClass('roo-grid-container');
34238 if(config.toolbar){
34239 var tool_el = this.wrapper.createChild();
34240 this.toolbar = Roo.factory(config.toolbar);
34242 if (config.toolbar.items) {
34243 ti = config.toolbar.items ;
34244 delete config.toolbar.items ;
34248 this.toolbar.render(tool_el);
34249 for(var i =0;i < ti.length;i++) {
34250 // Roo.log(['add child', items[i]]);
34251 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34253 this.toolbar.items = nitems;
34255 delete config.toolbar;
34258 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34259 config.grid.scrollBody = true;;
34260 config.grid.monitorWindowResize = false; // turn off autosizing
34261 config.grid.autoHeight = false;
34262 config.grid.autoWidth = false;
34264 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34266 if (config.background) {
34267 // render grid on panel activation (if panel background)
34268 this.on('activate', function(gp) {
34269 if (!gp.grid.rendered) {
34270 gp.grid.render(el);
34271 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34277 this.grid.render(this.wrapper);
34278 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34281 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34282 // ??? needed ??? config.el = this.wrapper;
34287 // xtype created footer. - not sure if will work as we normally have to render first..
34288 if (this.footer && !this.footer.el && this.footer.xtype) {
34290 var ctr = this.grid.getView().getFooterPanel(true);
34291 this.footer.dataSource = this.grid.dataSource;
34292 this.footer = Roo.factory(this.footer, Roo);
34293 this.footer.render(ctr);
34303 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34304 getId : function(){
34305 return this.grid.id;
34309 * Returns the grid for this panel
34310 * @return {Roo.bootstrap.Table}
34312 getGrid : function(){
34316 setSize : function(width, height){
34317 if(!this.ignoreResize(width, height)){
34318 var grid = this.grid;
34319 var size = this.adjustForComponents(width, height);
34320 var gridel = grid.getGridEl();
34321 gridel.setSize(size.width, size.height);
34323 var thd = grid.getGridEl().select('thead',true).first();
34324 var tbd = grid.getGridEl().select('tbody', true).first();
34326 tbd.setSize(width, height - thd.getHeight());
34335 beforeSlide : function(){
34336 this.grid.getView().scroller.clip();
34339 afterSlide : function(){
34340 this.grid.getView().scroller.unclip();
34343 destroy : function(){
34344 this.grid.destroy();
34346 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
34351 * @class Roo.bootstrap.panel.Nest
34352 * @extends Roo.bootstrap.panel.Content
34354 * Create a new Panel, that can contain a layout.Border.
34357 * @param {Roo.BorderLayout} layout The layout for this panel
34358 * @param {String/Object} config A string to set only the title or a config object
34360 Roo.bootstrap.panel.Nest = function(config)
34362 // construct with only one argument..
34363 /* FIXME - implement nicer consturctors
34364 if (layout.layout) {
34366 layout = config.layout;
34367 delete config.layout;
34369 if (layout.xtype && !layout.getEl) {
34370 // then layout needs constructing..
34371 layout = Roo.factory(layout, Roo);
34375 config.el = config.layout.getEl();
34377 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34379 config.layout.monitorWindowResize = false; // turn off autosizing
34380 this.layout = config.layout;
34381 this.layout.getEl().addClass("roo-layout-nested-layout");
34388 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34390 setSize : function(width, height){
34391 if(!this.ignoreResize(width, height)){
34392 var size = this.adjustForComponents(width, height);
34393 var el = this.layout.getEl();
34394 el.setSize(size.width, size.height);
34395 var touch = el.dom.offsetWidth;
34396 this.layout.layout();
34397 // ie requires a double layout on the first pass
34398 if(Roo.isIE && !this.initialized){
34399 this.initialized = true;
34400 this.layout.layout();
34405 // activate all subpanels if not currently active..
34407 setActiveState : function(active){
34408 this.active = active;
34410 this.fireEvent("deactivate", this);
34414 this.fireEvent("activate", this);
34415 // not sure if this should happen before or after..
34416 if (!this.layout) {
34417 return; // should not happen..
34420 for (var r in this.layout.regions) {
34421 reg = this.layout.getRegion(r);
34422 if (reg.getActivePanel()) {
34423 //reg.showPanel(reg.getActivePanel()); // force it to activate..
34424 reg.setActivePanel(reg.getActivePanel());
34427 if (!reg.panels.length) {
34430 reg.showPanel(reg.getPanel(0));
34439 * Returns the nested BorderLayout for this panel
34440 * @return {Roo.BorderLayout}
34442 getLayout : function(){
34443 return this.layout;
34447 * Adds a xtype elements to the layout of the nested panel
34451 xtype : 'ContentPanel',
34458 xtype : 'NestedLayoutPanel',
34464 items : [ ... list of content panels or nested layout panels.. ]
34468 * @param {Object} cfg Xtype definition of item to add.
34470 addxtype : function(cfg) {
34471 return this.layout.addxtype(cfg);
34476 * Ext JS Library 1.1.1
34477 * Copyright(c) 2006-2007, Ext JS, LLC.
34479 * Originally Released Under LGPL - original licence link has changed is not relivant.
34482 * <script type="text/javascript">
34485 * @class Roo.TabPanel
34486 * @extends Roo.util.Observable
34487 * A lightweight tab container.
34491 // basic tabs 1, built from existing content
34492 var tabs = new Roo.TabPanel("tabs1");
34493 tabs.addTab("script", "View Script");
34494 tabs.addTab("markup", "View Markup");
34495 tabs.activate("script");
34497 // more advanced tabs, built from javascript
34498 var jtabs = new Roo.TabPanel("jtabs");
34499 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34501 // set up the UpdateManager
34502 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34503 var updater = tab2.getUpdateManager();
34504 updater.setDefaultUrl("ajax1.htm");
34505 tab2.on('activate', updater.refresh, updater, true);
34507 // Use setUrl for Ajax loading
34508 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34509 tab3.setUrl("ajax2.htm", null, true);
34512 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34515 jtabs.activate("jtabs-1");
34518 * Create a new TabPanel.
34519 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34520 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34522 Roo.bootstrap.panel.Tabs = function(config){
34524 * The container element for this TabPanel.
34525 * @type Roo.Element
34527 this.el = Roo.get(config.el);
34530 if(typeof config == "boolean"){
34531 this.tabPosition = config ? "bottom" : "top";
34533 Roo.apply(this, config);
34537 if(this.tabPosition == "bottom"){
34538 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34539 this.el.addClass("roo-tabs-bottom");
34541 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34542 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34543 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34545 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34547 if(this.tabPosition != "bottom"){
34548 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34549 * @type Roo.Element
34551 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34552 this.el.addClass("roo-tabs-top");
34556 this.bodyEl.setStyle("position", "relative");
34558 this.active = null;
34559 this.activateDelegate = this.activate.createDelegate(this);
34564 * Fires when the active tab changes
34565 * @param {Roo.TabPanel} this
34566 * @param {Roo.TabPanelItem} activePanel The new active tab
34570 * @event beforetabchange
34571 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34572 * @param {Roo.TabPanel} this
34573 * @param {Object} e Set cancel to true on this object to cancel the tab change
34574 * @param {Roo.TabPanelItem} tab The tab being changed to
34576 "beforetabchange" : true
34579 Roo.EventManager.onWindowResize(this.onResize, this);
34580 this.cpad = this.el.getPadding("lr");
34581 this.hiddenCount = 0;
34584 // toolbar on the tabbar support...
34585 if (this.toolbar) {
34586 alert("no toolbar support yet");
34587 this.toolbar = false;
34589 var tcfg = this.toolbar;
34590 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
34591 this.toolbar = new Roo.Toolbar(tcfg);
34592 if (Roo.isSafari) {
34593 var tbl = tcfg.container.child('table', true);
34594 tbl.setAttribute('width', '100%');
34602 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34605 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34607 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34609 tabPosition : "top",
34611 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34613 currentTabWidth : 0,
34615 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34619 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34623 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34625 preferredTabWidth : 175,
34627 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34629 resizeTabs : false,
34631 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34633 monitorResize : true,
34635 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
34640 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34641 * @param {String} id The id of the div to use <b>or create</b>
34642 * @param {String} text The text for the tab
34643 * @param {String} content (optional) Content to put in the TabPanelItem body
34644 * @param {Boolean} closable (optional) True to create a close icon on the tab
34645 * @return {Roo.TabPanelItem} The created TabPanelItem
34647 addTab : function(id, text, content, closable)
34649 var item = new Roo.bootstrap.panel.TabItem({
34653 closable : closable
34655 this.addTabItem(item);
34657 item.setContent(content);
34663 * Returns the {@link Roo.TabPanelItem} with the specified id/index
34664 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34665 * @return {Roo.TabPanelItem}
34667 getTab : function(id){
34668 return this.items[id];
34672 * Hides the {@link Roo.TabPanelItem} with the specified id/index
34673 * @param {String/Number} id The id or index of the TabPanelItem to hide.
34675 hideTab : function(id){
34676 var t = this.items[id];
34679 this.hiddenCount++;
34680 this.autoSizeTabs();
34685 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34686 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34688 unhideTab : function(id){
34689 var t = this.items[id];
34691 t.setHidden(false);
34692 this.hiddenCount--;
34693 this.autoSizeTabs();
34698 * Adds an existing {@link Roo.TabPanelItem}.
34699 * @param {Roo.TabPanelItem} item The TabPanelItem to add
34701 addTabItem : function(item){
34702 this.items[item.id] = item;
34703 this.items.push(item);
34704 // if(this.resizeTabs){
34705 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34706 // this.autoSizeTabs();
34708 // item.autoSize();
34713 * Removes a {@link Roo.TabPanelItem}.
34714 * @param {String/Number} id The id or index of the TabPanelItem to remove.
34716 removeTab : function(id){
34717 var items = this.items;
34718 var tab = items[id];
34719 if(!tab) { return; }
34720 var index = items.indexOf(tab);
34721 if(this.active == tab && items.length > 1){
34722 var newTab = this.getNextAvailable(index);
34727 this.stripEl.dom.removeChild(tab.pnode.dom);
34728 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34729 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34731 items.splice(index, 1);
34732 delete this.items[tab.id];
34733 tab.fireEvent("close", tab);
34734 tab.purgeListeners();
34735 this.autoSizeTabs();
34738 getNextAvailable : function(start){
34739 var items = this.items;
34741 // look for a next tab that will slide over to
34742 // replace the one being removed
34743 while(index < items.length){
34744 var item = items[++index];
34745 if(item && !item.isHidden()){
34749 // if one isn't found select the previous tab (on the left)
34752 var item = items[--index];
34753 if(item && !item.isHidden()){
34761 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34762 * @param {String/Number} id The id or index of the TabPanelItem to disable.
34764 disableTab : function(id){
34765 var tab = this.items[id];
34766 if(tab && this.active != tab){
34772 * Enables a {@link Roo.TabPanelItem} that is disabled.
34773 * @param {String/Number} id The id or index of the TabPanelItem to enable.
34775 enableTab : function(id){
34776 var tab = this.items[id];
34781 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34782 * @param {String/Number} id The id or index of the TabPanelItem to activate.
34783 * @return {Roo.TabPanelItem} The TabPanelItem.
34785 activate : function(id){
34786 var tab = this.items[id];
34790 if(tab == this.active || tab.disabled){
34794 this.fireEvent("beforetabchange", this, e, tab);
34795 if(e.cancel !== true && !tab.disabled){
34797 this.active.hide();
34799 this.active = this.items[id];
34800 this.active.show();
34801 this.fireEvent("tabchange", this, this.active);
34807 * Gets the active {@link Roo.TabPanelItem}.
34808 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34810 getActiveTab : function(){
34811 return this.active;
34815 * Updates the tab body element to fit the height of the container element
34816 * for overflow scrolling
34817 * @param {Number} targetHeight (optional) Override the starting height from the elements height
34819 syncHeight : function(targetHeight){
34820 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34821 var bm = this.bodyEl.getMargins();
34822 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34823 this.bodyEl.setHeight(newHeight);
34827 onResize : function(){
34828 if(this.monitorResize){
34829 this.autoSizeTabs();
34834 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34836 beginUpdate : function(){
34837 this.updating = true;
34841 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34843 endUpdate : function(){
34844 this.updating = false;
34845 this.autoSizeTabs();
34849 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34851 autoSizeTabs : function(){
34852 var count = this.items.length;
34853 var vcount = count - this.hiddenCount;
34854 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34857 var w = Math.max(this.el.getWidth() - this.cpad, 10);
34858 var availWidth = Math.floor(w / vcount);
34859 var b = this.stripBody;
34860 if(b.getWidth() > w){
34861 var tabs = this.items;
34862 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34863 if(availWidth < this.minTabWidth){
34864 /*if(!this.sleft){ // incomplete scrolling code
34865 this.createScrollButtons();
34868 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34871 if(this.currentTabWidth < this.preferredTabWidth){
34872 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34878 * Returns the number of tabs in this TabPanel.
34881 getCount : function(){
34882 return this.items.length;
34886 * Resizes all the tabs to the passed width
34887 * @param {Number} The new width
34889 setTabWidth : function(width){
34890 this.currentTabWidth = width;
34891 for(var i = 0, len = this.items.length; i < len; i++) {
34892 if(!this.items[i].isHidden()) {
34893 this.items[i].setWidth(width);
34899 * Destroys this TabPanel
34900 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34902 destroy : function(removeEl){
34903 Roo.EventManager.removeResizeListener(this.onResize, this);
34904 for(var i = 0, len = this.items.length; i < len; i++){
34905 this.items[i].purgeListeners();
34907 if(removeEl === true){
34908 this.el.update("");
34913 createStrip : function(container)
34915 var strip = document.createElement("nav");
34916 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34917 container.appendChild(strip);
34921 createStripList : function(strip)
34923 // div wrapper for retard IE
34924 // returns the "tr" element.
34925 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34926 //'<div class="x-tabs-strip-wrap">'+
34927 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34928 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34929 return strip.firstChild; //.firstChild.firstChild.firstChild;
34931 createBody : function(container)
34933 var body = document.createElement("div");
34934 Roo.id(body, "tab-body");
34935 //Roo.fly(body).addClass("x-tabs-body");
34936 Roo.fly(body).addClass("tab-content");
34937 container.appendChild(body);
34940 createItemBody :function(bodyEl, id){
34941 var body = Roo.getDom(id);
34943 body = document.createElement("div");
34946 //Roo.fly(body).addClass("x-tabs-item-body");
34947 Roo.fly(body).addClass("tab-pane");
34948 bodyEl.insertBefore(body, bodyEl.firstChild);
34952 createStripElements : function(stripEl, text, closable)
34954 var td = document.createElement("li"); // was td..
34955 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34956 //stripEl.appendChild(td);
34958 td.className = "x-tabs-closable";
34959 if(!this.closeTpl){
34960 this.closeTpl = new Roo.Template(
34961 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34962 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34963 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
34966 var el = this.closeTpl.overwrite(td, {"text": text});
34967 var close = el.getElementsByTagName("div")[0];
34968 var inner = el.getElementsByTagName("em")[0];
34969 return {"el": el, "close": close, "inner": inner};
34972 // not sure what this is..
34974 //this.tabTpl = new Roo.Template(
34975 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34976 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34978 this.tabTpl = new Roo.Template(
34980 '<span unselectable="on"' +
34981 (this.disableTooltips ? '' : ' title="{text}"') +
34982 ' >{text}</span></span></a>'
34986 var el = this.tabTpl.overwrite(td, {"text": text});
34987 var inner = el.getElementsByTagName("span")[0];
34988 return {"el": el, "inner": inner};
34996 * @class Roo.TabPanelItem
34997 * @extends Roo.util.Observable
34998 * Represents an individual item (tab plus body) in a TabPanel.
34999 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35000 * @param {String} id The id of this TabPanelItem
35001 * @param {String} text The text for the tab of this TabPanelItem
35002 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35004 Roo.bootstrap.panel.TabItem = function(config){
35006 * The {@link Roo.TabPanel} this TabPanelItem belongs to
35007 * @type Roo.TabPanel
35009 this.tabPanel = config.panel;
35011 * The id for this TabPanelItem
35014 this.id = config.id;
35016 this.disabled = false;
35018 this.text = config.text;
35020 this.loaded = false;
35021 this.closable = config.closable;
35024 * The body element for this TabPanelItem.
35025 * @type Roo.Element
35027 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35028 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35029 this.bodyEl.setStyle("display", "block");
35030 this.bodyEl.setStyle("zoom", "1");
35031 //this.hideAction();
35033 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35035 this.el = Roo.get(els.el);
35036 this.inner = Roo.get(els.inner, true);
35037 this.textEl = Roo.get(this.el.dom.firstChild, true);
35038 this.pnode = Roo.get(els.el.parentNode, true);
35039 this.el.on("mousedown", this.onTabMouseDown, this);
35040 this.el.on("click", this.onTabClick, this);
35042 if(config.closable){
35043 var c = Roo.get(els.close, true);
35044 c.dom.title = this.closeText;
35045 c.addClassOnOver("close-over");
35046 c.on("click", this.closeClick, this);
35052 * Fires when this tab becomes the active tab.
35053 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35054 * @param {Roo.TabPanelItem} this
35058 * @event beforeclose
35059 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35060 * @param {Roo.TabPanelItem} this
35061 * @param {Object} e Set cancel to true on this object to cancel the close.
35063 "beforeclose": true,
35066 * Fires when this tab is closed.
35067 * @param {Roo.TabPanelItem} this
35071 * @event deactivate
35072 * Fires when this tab is no longer the active tab.
35073 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35074 * @param {Roo.TabPanelItem} this
35076 "deactivate" : true
35078 this.hidden = false;
35080 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35083 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35085 purgeListeners : function(){
35086 Roo.util.Observable.prototype.purgeListeners.call(this);
35087 this.el.removeAllListeners();
35090 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35093 this.pnode.addClass("active");
35096 this.tabPanel.stripWrap.repaint();
35098 this.fireEvent("activate", this.tabPanel, this);
35102 * Returns true if this tab is the active tab.
35103 * @return {Boolean}
35105 isActive : function(){
35106 return this.tabPanel.getActiveTab() == this;
35110 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35113 this.pnode.removeClass("active");
35115 this.fireEvent("deactivate", this.tabPanel, this);
35118 hideAction : function(){
35119 this.bodyEl.hide();
35120 this.bodyEl.setStyle("position", "absolute");
35121 this.bodyEl.setLeft("-20000px");
35122 this.bodyEl.setTop("-20000px");
35125 showAction : function(){
35126 this.bodyEl.setStyle("position", "relative");
35127 this.bodyEl.setTop("");
35128 this.bodyEl.setLeft("");
35129 this.bodyEl.show();
35133 * Set the tooltip for the tab.
35134 * @param {String} tooltip The tab's tooltip
35136 setTooltip : function(text){
35137 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35138 this.textEl.dom.qtip = text;
35139 this.textEl.dom.removeAttribute('title');
35141 this.textEl.dom.title = text;
35145 onTabClick : function(e){
35146 e.preventDefault();
35147 this.tabPanel.activate(this.id);
35150 onTabMouseDown : function(e){
35151 e.preventDefault();
35152 this.tabPanel.activate(this.id);
35155 getWidth : function(){
35156 return this.inner.getWidth();
35159 setWidth : function(width){
35160 var iwidth = width - this.pnode.getPadding("lr");
35161 this.inner.setWidth(iwidth);
35162 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35163 this.pnode.setWidth(width);
35167 * Show or hide the tab
35168 * @param {Boolean} hidden True to hide or false to show.
35170 setHidden : function(hidden){
35171 this.hidden = hidden;
35172 this.pnode.setStyle("display", hidden ? "none" : "");
35176 * Returns true if this tab is "hidden"
35177 * @return {Boolean}
35179 isHidden : function(){
35180 return this.hidden;
35184 * Returns the text for this tab
35187 getText : function(){
35191 autoSize : function(){
35192 //this.el.beginMeasure();
35193 this.textEl.setWidth(1);
35195 * #2804 [new] Tabs in Roojs
35196 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35198 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35199 //this.el.endMeasure();
35203 * Sets the text for the tab (Note: this also sets the tooltip text)
35204 * @param {String} text The tab's text and tooltip
35206 setText : function(text){
35208 this.textEl.update(text);
35209 this.setTooltip(text);
35210 //if(!this.tabPanel.resizeTabs){
35211 // this.autoSize();
35215 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35217 activate : function(){
35218 this.tabPanel.activate(this.id);
35222 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35224 disable : function(){
35225 if(this.tabPanel.active != this){
35226 this.disabled = true;
35227 this.pnode.addClass("disabled");
35232 * Enables this TabPanelItem if it was previously disabled.
35234 enable : function(){
35235 this.disabled = false;
35236 this.pnode.removeClass("disabled");
35240 * Sets the content for this TabPanelItem.
35241 * @param {String} content The content
35242 * @param {Boolean} loadScripts true to look for and load scripts
35244 setContent : function(content, loadScripts){
35245 this.bodyEl.update(content, loadScripts);
35249 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35250 * @return {Roo.UpdateManager} The UpdateManager
35252 getUpdateManager : function(){
35253 return this.bodyEl.getUpdateManager();
35257 * Set a URL to be used to load the content for this TabPanelItem.
35258 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35259 * @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)
35260 * @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)
35261 * @return {Roo.UpdateManager} The UpdateManager
35263 setUrl : function(url, params, loadOnce){
35264 if(this.refreshDelegate){
35265 this.un('activate', this.refreshDelegate);
35267 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35268 this.on("activate", this.refreshDelegate);
35269 return this.bodyEl.getUpdateManager();
35273 _handleRefresh : function(url, params, loadOnce){
35274 if(!loadOnce || !this.loaded){
35275 var updater = this.bodyEl.getUpdateManager();
35276 updater.update(url, params, this._setLoaded.createDelegate(this));
35281 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
35282 * Will fail silently if the setUrl method has not been called.
35283 * This does not activate the panel, just updates its content.
35285 refresh : function(){
35286 if(this.refreshDelegate){
35287 this.loaded = false;
35288 this.refreshDelegate();
35293 _setLoaded : function(){
35294 this.loaded = true;
35298 closeClick : function(e){
35301 this.fireEvent("beforeclose", this, o);
35302 if(o.cancel !== true){
35303 this.tabPanel.removeTab(this.id);
35307 * The text displayed in the tooltip for the close icon.
35310 closeText : "Close this tab"