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.growclickrid.on("mousedown", this.handleMouseDown, this);
22424 //}else{ // allow click to work like normal
22425 // this.grid.on("rowclick", this.handleDragableRowClick, this);
22427 this.grid.on("rowclick", this.handleMouseDown, this);
22429 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22430 "up" : function(e){
22432 this.selectPrevious(e.shiftKey);
22433 }else if(this.last !== false && this.lastActive !== false){
22434 var last = this.last;
22435 this.selectRange(this.last, this.lastActive-1);
22436 this.grid.getView().focusRow(this.lastActive);
22437 if(last !== false){
22441 this.selectFirstRow();
22443 this.fireEvent("afterselectionchange", this);
22445 "down" : function(e){
22447 this.selectNext(e.shiftKey);
22448 }else if(this.last !== false && this.lastActive !== false){
22449 var last = this.last;
22450 this.selectRange(this.last, this.lastActive+1);
22451 this.grid.getView().focusRow(this.lastActive);
22452 if(last !== false){
22456 this.selectFirstRow();
22458 this.fireEvent("afterselectionchange", this);
22463 var view = this.grid.view;
22464 view.on("refresh", this.onRefresh, this);
22465 view.on("rowupdated", this.onRowUpdated, this);
22466 view.on("rowremoved", this.onRemove, this);
22471 onRefresh : function(){
22472 var ds = this.grid.dataSource, i, v = this.grid.view;
22473 var s = this.selections;
22474 s.each(function(r){
22475 if((i = ds.indexOfId(r.id)) != -1){
22484 onRemove : function(v, index, r){
22485 this.selections.remove(r);
22489 onRowUpdated : function(v, index, r){
22490 if(this.isSelected(r)){
22491 v.onRowSelect(index);
22497 * @param {Array} records The records to select
22498 * @param {Boolean} keepExisting (optional) True to keep existing selections
22500 selectRecords : function(records, keepExisting){
22502 this.clearSelections();
22504 var ds = this.grid.dataSource;
22505 for(var i = 0, len = records.length; i < len; i++){
22506 this.selectRow(ds.indexOf(records[i]), true);
22511 * Gets the number of selected rows.
22514 getCount : function(){
22515 return this.selections.length;
22519 * Selects the first row in the grid.
22521 selectFirstRow : function(){
22526 * Select the last row.
22527 * @param {Boolean} keepExisting (optional) True to keep existing selections
22529 selectLastRow : function(keepExisting){
22530 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22534 * Selects the row immediately following the last selected row.
22535 * @param {Boolean} keepExisting (optional) True to keep existing selections
22537 selectNext : function(keepExisting){
22538 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22539 this.selectRow(this.last+1, keepExisting);
22540 this.grid.getView().focusRow(this.last);
22545 * Selects the row that precedes the last selected row.
22546 * @param {Boolean} keepExisting (optional) True to keep existing selections
22548 selectPrevious : function(keepExisting){
22550 this.selectRow(this.last-1, keepExisting);
22551 this.grid.getView().focusRow(this.last);
22556 * Returns the selected records
22557 * @return {Array} Array of selected records
22559 getSelections : function(){
22560 return [].concat(this.selections.items);
22564 * Returns the first selected record.
22567 getSelected : function(){
22568 return this.selections.itemAt(0);
22573 * Clears all selections.
22575 clearSelections : function(fast){
22580 var ds = this.grid.dataSource;
22581 var s = this.selections;
22582 s.each(function(r){
22583 this.deselectRow(ds.indexOfId(r.id));
22587 this.selections.clear();
22594 * Selects all rows.
22596 selectAll : function(){
22600 this.selections.clear();
22601 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22602 this.selectRow(i, true);
22607 * Returns True if there is a selection.
22608 * @return {Boolean}
22610 hasSelection : function(){
22611 return this.selections.length > 0;
22615 * Returns True if the specified row is selected.
22616 * @param {Number/Record} record The record or index of the record to check
22617 * @return {Boolean}
22619 isSelected : function(index){
22620 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22621 return (r && this.selections.key(r.id) ? true : false);
22625 * Returns True if the specified record id is selected.
22626 * @param {String} id The id of record to check
22627 * @return {Boolean}
22629 isIdSelected : function(id){
22630 return (this.selections.key(id) ? true : false);
22634 handleMouseDown : function(e, t){
22635 var view = this.grid.getView(), rowIndex;
22636 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22639 if(e.shiftKey && this.last !== false){
22640 var last = this.last;
22641 this.selectRange(last, rowIndex, e.ctrlKey);
22642 this.last = last; // reset the last
22643 view.focusRow(rowIndex);
22645 var isSelected = this.isSelected(rowIndex);
22646 if(e.button !== 0 && isSelected){
22647 view.focusRow(rowIndex);
22648 }else if(e.ctrlKey && isSelected){
22649 this.deselectRow(rowIndex);
22650 }else if(!isSelected){
22651 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22652 view.focusRow(rowIndex);
22655 this.fireEvent("afterselectionchange", this);
22658 handleDragableRowClick : function(grid, rowIndex, e)
22660 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22661 this.selectRow(rowIndex, false);
22662 grid.view.focusRow(rowIndex);
22663 this.fireEvent("afterselectionchange", this);
22668 * Selects multiple rows.
22669 * @param {Array} rows Array of the indexes of the row to select
22670 * @param {Boolean} keepExisting (optional) True to keep existing selections
22672 selectRows : function(rows, keepExisting){
22674 this.clearSelections();
22676 for(var i = 0, len = rows.length; i < len; i++){
22677 this.selectRow(rows[i], true);
22682 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22683 * @param {Number} startRow The index of the first row in the range
22684 * @param {Number} endRow The index of the last row in the range
22685 * @param {Boolean} keepExisting (optional) True to retain existing selections
22687 selectRange : function(startRow, endRow, keepExisting){
22692 this.clearSelections();
22694 if(startRow <= endRow){
22695 for(var i = startRow; i <= endRow; i++){
22696 this.selectRow(i, true);
22699 for(var i = startRow; i >= endRow; i--){
22700 this.selectRow(i, true);
22706 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22707 * @param {Number} startRow The index of the first row in the range
22708 * @param {Number} endRow The index of the last row in the range
22710 deselectRange : function(startRow, endRow, preventViewNotify){
22714 for(var i = startRow; i <= endRow; i++){
22715 this.deselectRow(i, preventViewNotify);
22721 * @param {Number} row The index of the row to select
22722 * @param {Boolean} keepExisting (optional) True to keep existing selections
22724 selectRow : function(index, keepExisting, preventViewNotify){
22725 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22728 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22729 if(!keepExisting || this.singleSelect){
22730 this.clearSelections();
22732 var r = this.grid.dataSource.getAt(index);
22733 this.selections.add(r);
22734 this.last = this.lastActive = index;
22735 if(!preventViewNotify){
22736 this.grid.getView().onRowSelect(index);
22738 this.fireEvent("rowselect", this, index, r);
22739 this.fireEvent("selectionchange", this);
22745 * @param {Number} row The index of the row to deselect
22747 deselectRow : function(index, preventViewNotify){
22751 if(this.last == index){
22754 if(this.lastActive == index){
22755 this.lastActive = false;
22757 var r = this.grid.dataSource.getAt(index);
22758 this.selections.remove(r);
22759 if(!preventViewNotify){
22760 this.grid.getView().onRowDeselect(index);
22762 this.fireEvent("rowdeselect", this, index);
22763 this.fireEvent("selectionchange", this);
22767 restoreLast : function(){
22769 this.last = this._last;
22774 acceptsNav : function(row, col, cm){
22775 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22779 onEditorKey : function(field, e){
22780 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22785 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22787 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22789 }else if(k == e.ENTER && !e.ctrlKey){
22793 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22795 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22797 }else if(k == e.ESC){
22801 g.startEditing(newCell[0], newCell[1]);
22806 * Ext JS Library 1.1.1
22807 * Copyright(c) 2006-2007, Ext JS, LLC.
22809 * Originally Released Under LGPL - original licence link has changed is not relivant.
22812 * <script type="text/javascript">
22816 * @class Roo.bootstrap.PagingToolbar
22817 * @extends Roo.bootstrap.NavSimplebar
22818 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22820 * Create a new PagingToolbar
22821 * @param {Object} config The config object
22822 * @param {Roo.data.Store} store
22824 Roo.bootstrap.PagingToolbar = function(config)
22826 // old args format still supported... - xtype is prefered..
22827 // created from xtype...
22829 this.ds = config.dataSource;
22831 if (config.store && !this.ds) {
22832 this.store= Roo.factory(config.store, Roo.data);
22833 this.ds = this.store;
22834 this.ds.xmodule = this.xmodule || false;
22837 this.toolbarItems = [];
22838 if (config.items) {
22839 this.toolbarItems = config.items;
22842 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22847 this.bind(this.ds);
22850 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22854 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22856 * @cfg {Roo.data.Store} dataSource
22857 * The underlying data store providing the paged data
22860 * @cfg {String/HTMLElement/Element} container
22861 * container The id or element that will contain the toolbar
22864 * @cfg {Boolean} displayInfo
22865 * True to display the displayMsg (defaults to false)
22868 * @cfg {Number} pageSize
22869 * The number of records to display per page (defaults to 20)
22873 * @cfg {String} displayMsg
22874 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22876 displayMsg : 'Displaying {0} - {1} of {2}',
22878 * @cfg {String} emptyMsg
22879 * The message to display when no records are found (defaults to "No data to display")
22881 emptyMsg : 'No data to display',
22883 * Customizable piece of the default paging text (defaults to "Page")
22886 beforePageText : "Page",
22888 * Customizable piece of the default paging text (defaults to "of %0")
22891 afterPageText : "of {0}",
22893 * Customizable piece of the default paging text (defaults to "First Page")
22896 firstText : "First Page",
22898 * Customizable piece of the default paging text (defaults to "Previous Page")
22901 prevText : "Previous Page",
22903 * Customizable piece of the default paging text (defaults to "Next Page")
22906 nextText : "Next Page",
22908 * Customizable piece of the default paging text (defaults to "Last Page")
22911 lastText : "Last Page",
22913 * Customizable piece of the default paging text (defaults to "Refresh")
22916 refreshText : "Refresh",
22920 onRender : function(ct, position)
22922 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22923 this.navgroup.parentId = this.id;
22924 this.navgroup.onRender(this.el, null);
22925 // add the buttons to the navgroup
22927 if(this.displayInfo){
22928 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22929 this.displayEl = this.el.select('.x-paging-info', true).first();
22930 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22931 // this.displayEl = navel.el.select('span',true).first();
22937 Roo.each(_this.buttons, function(e){ // this might need to use render????
22938 Roo.factory(e).onRender(_this.el, null);
22942 Roo.each(_this.toolbarItems, function(e) {
22943 _this.navgroup.addItem(e);
22947 this.first = this.navgroup.addItem({
22948 tooltip: this.firstText,
22950 icon : 'fa fa-backward',
22952 preventDefault: true,
22953 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22956 this.prev = this.navgroup.addItem({
22957 tooltip: this.prevText,
22959 icon : 'fa fa-step-backward',
22961 preventDefault: true,
22962 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22964 //this.addSeparator();
22967 var field = this.navgroup.addItem( {
22969 cls : 'x-paging-position',
22971 html : this.beforePageText +
22972 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22973 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22976 this.field = field.el.select('input', true).first();
22977 this.field.on("keydown", this.onPagingKeydown, this);
22978 this.field.on("focus", function(){this.dom.select();});
22981 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22982 //this.field.setHeight(18);
22983 //this.addSeparator();
22984 this.next = this.navgroup.addItem({
22985 tooltip: this.nextText,
22987 html : ' <i class="fa fa-step-forward">',
22989 preventDefault: true,
22990 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22992 this.last = this.navgroup.addItem({
22993 tooltip: this.lastText,
22994 icon : 'fa fa-forward',
22997 preventDefault: true,
22998 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
23000 //this.addSeparator();
23001 this.loading = this.navgroup.addItem({
23002 tooltip: this.refreshText,
23003 icon: 'fa fa-refresh',
23004 preventDefault: true,
23005 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23011 updateInfo : function(){
23012 if(this.displayEl){
23013 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23014 var msg = count == 0 ?
23018 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23020 this.displayEl.update(msg);
23025 onLoad : function(ds, r, o){
23026 this.cursor = o.params ? o.params.start : 0;
23027 var d = this.getPageData(),
23031 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23032 this.field.dom.value = ap;
23033 this.first.setDisabled(ap == 1);
23034 this.prev.setDisabled(ap == 1);
23035 this.next.setDisabled(ap == ps);
23036 this.last.setDisabled(ap == ps);
23037 this.loading.enable();
23042 getPageData : function(){
23043 var total = this.ds.getTotalCount();
23046 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23047 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23052 onLoadError : function(){
23053 this.loading.enable();
23057 onPagingKeydown : function(e){
23058 var k = e.getKey();
23059 var d = this.getPageData();
23061 var v = this.field.dom.value, pageNum;
23062 if(!v || isNaN(pageNum = parseInt(v, 10))){
23063 this.field.dom.value = d.activePage;
23066 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23067 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23070 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))
23072 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23073 this.field.dom.value = pageNum;
23074 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23077 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23079 var v = this.field.dom.value, pageNum;
23080 var increment = (e.shiftKey) ? 10 : 1;
23081 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23084 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23085 this.field.dom.value = d.activePage;
23088 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23090 this.field.dom.value = parseInt(v, 10) + increment;
23091 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23092 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23099 beforeLoad : function(){
23101 this.loading.disable();
23106 onClick : function(which){
23115 ds.load({params:{start: 0, limit: this.pageSize}});
23118 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23121 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23124 var total = ds.getTotalCount();
23125 var extra = total % this.pageSize;
23126 var lastStart = extra ? (total - extra) : total-this.pageSize;
23127 ds.load({params:{start: lastStart, limit: this.pageSize}});
23130 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23136 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23137 * @param {Roo.data.Store} store The data store to unbind
23139 unbind : function(ds){
23140 ds.un("beforeload", this.beforeLoad, this);
23141 ds.un("load", this.onLoad, this);
23142 ds.un("loadexception", this.onLoadError, this);
23143 ds.un("remove", this.updateInfo, this);
23144 ds.un("add", this.updateInfo, this);
23145 this.ds = undefined;
23149 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23150 * @param {Roo.data.Store} store The data store to bind
23152 bind : function(ds){
23153 ds.on("beforeload", this.beforeLoad, this);
23154 ds.on("load", this.onLoad, this);
23155 ds.on("loadexception", this.onLoadError, this);
23156 ds.on("remove", this.updateInfo, this);
23157 ds.on("add", this.updateInfo, this);
23168 * @class Roo.bootstrap.MessageBar
23169 * @extends Roo.bootstrap.Component
23170 * Bootstrap MessageBar class
23171 * @cfg {String} html contents of the MessageBar
23172 * @cfg {String} weight (info | success | warning | danger) default info
23173 * @cfg {String} beforeClass insert the bar before the given class
23174 * @cfg {Boolean} closable (true | false) default false
23175 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23178 * Create a new Element
23179 * @param {Object} config The config object
23182 Roo.bootstrap.MessageBar = function(config){
23183 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23186 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23192 beforeClass: 'bootstrap-sticky-wrap',
23194 getAutoCreate : function(){
23198 cls: 'alert alert-dismissable alert-' + this.weight,
23203 html: this.html || ''
23209 cfg.cls += ' alert-messages-fixed';
23223 onRender : function(ct, position)
23225 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23228 var cfg = Roo.apply({}, this.getAutoCreate());
23232 cfg.cls += ' ' + this.cls;
23235 cfg.style = this.style;
23237 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23239 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23242 this.el.select('>button.close').on('click', this.hide, this);
23248 if (!this.rendered) {
23254 this.fireEvent('show', this);
23260 if (!this.rendered) {
23266 this.fireEvent('hide', this);
23269 update : function()
23271 // var e = this.el.dom.firstChild;
23273 // if(this.closable){
23274 // e = e.nextSibling;
23277 // e.data = this.html || '';
23279 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23295 * @class Roo.bootstrap.Graph
23296 * @extends Roo.bootstrap.Component
23297 * Bootstrap Graph class
23301 @cfg {String} graphtype bar | vbar | pie
23302 @cfg {number} g_x coodinator | centre x (pie)
23303 @cfg {number} g_y coodinator | centre y (pie)
23304 @cfg {number} g_r radius (pie)
23305 @cfg {number} g_height height of the chart (respected by all elements in the set)
23306 @cfg {number} g_width width of the chart (respected by all elements in the set)
23307 @cfg {Object} title The title of the chart
23310 -opts (object) options for the chart
23312 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23313 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23315 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.
23316 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23318 o stretch (boolean)
23320 -opts (object) options for the pie
23323 o startAngle (number)
23324 o endAngle (number)
23328 * Create a new Input
23329 * @param {Object} config The config object
23332 Roo.bootstrap.Graph = function(config){
23333 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23339 * The img click event for the img.
23340 * @param {Roo.EventObject} e
23346 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23357 //g_colors: this.colors,
23364 getAutoCreate : function(){
23375 onRender : function(ct,position){
23378 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23380 if (typeof(Raphael) == 'undefined') {
23381 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23385 this.raphael = Raphael(this.el.dom);
23387 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23388 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23389 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23390 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23392 r.text(160, 10, "Single Series Chart").attr(txtattr);
23393 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23394 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23395 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23397 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23398 r.barchart(330, 10, 300, 220, data1);
23399 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23400 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23403 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23404 // r.barchart(30, 30, 560, 250, xdata, {
23405 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23406 // axis : "0 0 1 1",
23407 // axisxlabels : xdata
23408 // //yvalues : cols,
23411 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23413 // this.load(null,xdata,{
23414 // axis : "0 0 1 1",
23415 // axisxlabels : xdata
23420 load : function(graphtype,xdata,opts)
23422 this.raphael.clear();
23424 graphtype = this.graphtype;
23429 var r = this.raphael,
23430 fin = function () {
23431 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23433 fout = function () {
23434 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23436 pfin = function() {
23437 this.sector.stop();
23438 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23441 this.label[0].stop();
23442 this.label[0].attr({ r: 7.5 });
23443 this.label[1].attr({ "font-weight": 800 });
23446 pfout = function() {
23447 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23450 this.label[0].animate({ r: 5 }, 500, "bounce");
23451 this.label[1].attr({ "font-weight": 400 });
23457 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23460 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23463 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23464 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23466 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23473 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23478 setTitle: function(o)
23483 initEvents: function() {
23486 this.el.on('click', this.onClick, this);
23490 onClick : function(e)
23492 Roo.log('img onclick');
23493 this.fireEvent('click', this, e);
23505 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23508 * @class Roo.bootstrap.dash.NumberBox
23509 * @extends Roo.bootstrap.Component
23510 * Bootstrap NumberBox class
23511 * @cfg {String} headline Box headline
23512 * @cfg {String} content Box content
23513 * @cfg {String} icon Box icon
23514 * @cfg {String} footer Footer text
23515 * @cfg {String} fhref Footer href
23518 * Create a new NumberBox
23519 * @param {Object} config The config object
23523 Roo.bootstrap.dash.NumberBox = function(config){
23524 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23528 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23537 getAutoCreate : function(){
23541 cls : 'small-box ',
23549 cls : 'roo-headline',
23550 html : this.headline
23554 cls : 'roo-content',
23555 html : this.content
23569 cls : 'ion ' + this.icon
23578 cls : 'small-box-footer',
23579 href : this.fhref || '#',
23583 cfg.cn.push(footer);
23590 onRender : function(ct,position){
23591 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23598 setHeadline: function (value)
23600 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23603 setFooter: function (value, href)
23605 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23608 this.el.select('a.small-box-footer',true).first().attr('href', href);
23613 setContent: function (value)
23615 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23618 initEvents: function()
23632 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23635 * @class Roo.bootstrap.dash.TabBox
23636 * @extends Roo.bootstrap.Component
23637 * Bootstrap TabBox class
23638 * @cfg {String} title Title of the TabBox
23639 * @cfg {String} icon Icon of the TabBox
23640 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23641 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23644 * Create a new TabBox
23645 * @param {Object} config The config object
23649 Roo.bootstrap.dash.TabBox = function(config){
23650 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23655 * When a pane is added
23656 * @param {Roo.bootstrap.dash.TabPane} pane
23660 * @event activatepane
23661 * When a pane is activated
23662 * @param {Roo.bootstrap.dash.TabPane} pane
23664 "activatepane" : true
23672 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23677 tabScrollable : false,
23679 getChildContainer : function()
23681 return this.el.select('.tab-content', true).first();
23684 getAutoCreate : function(){
23688 cls: 'pull-left header',
23696 cls: 'fa ' + this.icon
23702 cls: 'nav nav-tabs pull-right',
23708 if(this.tabScrollable){
23715 cls: 'nav nav-tabs pull-right',
23726 cls: 'nav-tabs-custom',
23731 cls: 'tab-content no-padding',
23739 initEvents : function()
23741 //Roo.log('add add pane handler');
23742 this.on('addpane', this.onAddPane, this);
23745 * Updates the box title
23746 * @param {String} html to set the title to.
23748 setTitle : function(value)
23750 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23752 onAddPane : function(pane)
23754 this.panes.push(pane);
23755 //Roo.log('addpane');
23757 // tabs are rendere left to right..
23758 if(!this.showtabs){
23762 var ctr = this.el.select('.nav-tabs', true).first();
23765 var existing = ctr.select('.nav-tab',true);
23766 var qty = existing.getCount();;
23769 var tab = ctr.createChild({
23771 cls : 'nav-tab' + (qty ? '' : ' active'),
23779 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23782 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23784 pane.el.addClass('active');
23789 onTabClick : function(ev,un,ob,pane)
23791 //Roo.log('tab - prev default');
23792 ev.preventDefault();
23795 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23796 pane.tab.addClass('active');
23797 //Roo.log(pane.title);
23798 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23799 // technically we should have a deactivate event.. but maybe add later.
23800 // and it should not de-activate the selected tab...
23801 this.fireEvent('activatepane', pane);
23802 pane.el.addClass('active');
23803 pane.fireEvent('activate');
23808 getActivePane : function()
23811 Roo.each(this.panes, function(p) {
23812 if(p.el.hasClass('active')){
23833 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23835 * @class Roo.bootstrap.TabPane
23836 * @extends Roo.bootstrap.Component
23837 * Bootstrap TabPane class
23838 * @cfg {Boolean} active (false | true) Default false
23839 * @cfg {String} title title of panel
23843 * Create a new TabPane
23844 * @param {Object} config The config object
23847 Roo.bootstrap.dash.TabPane = function(config){
23848 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23854 * When a pane is activated
23855 * @param {Roo.bootstrap.dash.TabPane} pane
23862 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23867 // the tabBox that this is attached to.
23870 getAutoCreate : function()
23878 cfg.cls += ' active';
23883 initEvents : function()
23885 //Roo.log('trigger add pane handler');
23886 this.parent().fireEvent('addpane', this)
23890 * Updates the tab title
23891 * @param {String} html to set the title to.
23893 setTitle: function(str)
23899 this.tab.select('a', true).first().dom.innerHTML = str;
23916 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23919 * @class Roo.bootstrap.menu.Menu
23920 * @extends Roo.bootstrap.Component
23921 * Bootstrap Menu class - container for Menu
23922 * @cfg {String} html Text of the menu
23923 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23924 * @cfg {String} icon Font awesome icon
23925 * @cfg {String} pos Menu align to (top | bottom) default bottom
23929 * Create a new Menu
23930 * @param {Object} config The config object
23934 Roo.bootstrap.menu.Menu = function(config){
23935 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23939 * @event beforeshow
23940 * Fires before this menu is displayed
23941 * @param {Roo.bootstrap.menu.Menu} this
23945 * @event beforehide
23946 * Fires before this menu is hidden
23947 * @param {Roo.bootstrap.menu.Menu} this
23952 * Fires after this menu is displayed
23953 * @param {Roo.bootstrap.menu.Menu} this
23958 * Fires after this menu is hidden
23959 * @param {Roo.bootstrap.menu.Menu} this
23964 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23965 * @param {Roo.bootstrap.menu.Menu} this
23966 * @param {Roo.EventObject} e
23973 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23977 weight : 'default',
23982 getChildContainer : function() {
23983 if(this.isSubMenu){
23987 return this.el.select('ul.dropdown-menu', true).first();
23990 getAutoCreate : function()
23995 cls : 'roo-menu-text',
24003 cls : 'fa ' + this.icon
24014 cls : 'dropdown-button btn btn-' + this.weight,
24019 cls : 'dropdown-toggle btn btn-' + this.weight,
24029 cls : 'dropdown-menu'
24035 if(this.pos == 'top'){
24036 cfg.cls += ' dropup';
24039 if(this.isSubMenu){
24042 cls : 'dropdown-menu'
24049 onRender : function(ct, position)
24051 this.isSubMenu = ct.hasClass('dropdown-submenu');
24053 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24056 initEvents : function()
24058 if(this.isSubMenu){
24062 this.hidden = true;
24064 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24065 this.triggerEl.on('click', this.onTriggerPress, this);
24067 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24068 this.buttonEl.on('click', this.onClick, this);
24074 if(this.isSubMenu){
24078 return this.el.select('ul.dropdown-menu', true).first();
24081 onClick : function(e)
24083 this.fireEvent("click", this, e);
24086 onTriggerPress : function(e)
24088 if (this.isVisible()) {
24095 isVisible : function(){
24096 return !this.hidden;
24101 this.fireEvent("beforeshow", this);
24103 this.hidden = false;
24104 this.el.addClass('open');
24106 Roo.get(document).on("mouseup", this.onMouseUp, this);
24108 this.fireEvent("show", this);
24115 this.fireEvent("beforehide", this);
24117 this.hidden = true;
24118 this.el.removeClass('open');
24120 Roo.get(document).un("mouseup", this.onMouseUp);
24122 this.fireEvent("hide", this);
24125 onMouseUp : function()
24139 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24142 * @class Roo.bootstrap.menu.Item
24143 * @extends Roo.bootstrap.Component
24144 * Bootstrap MenuItem class
24145 * @cfg {Boolean} submenu (true | false) default false
24146 * @cfg {String} html text of the item
24147 * @cfg {String} href the link
24148 * @cfg {Boolean} disable (true | false) default false
24149 * @cfg {Boolean} preventDefault (true | false) default true
24150 * @cfg {String} icon Font awesome icon
24151 * @cfg {String} pos Submenu align to (left | right) default right
24155 * Create a new Item
24156 * @param {Object} config The config object
24160 Roo.bootstrap.menu.Item = function(config){
24161 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24165 * Fires when the mouse is hovering over this menu
24166 * @param {Roo.bootstrap.menu.Item} this
24167 * @param {Roo.EventObject} e
24172 * Fires when the mouse exits this menu
24173 * @param {Roo.bootstrap.menu.Item} this
24174 * @param {Roo.EventObject} e
24180 * The raw click event for the entire grid.
24181 * @param {Roo.EventObject} e
24187 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24192 preventDefault: true,
24197 getAutoCreate : function()
24202 cls : 'roo-menu-item-text',
24210 cls : 'fa ' + this.icon
24219 href : this.href || '#',
24226 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24230 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24232 if(this.pos == 'left'){
24233 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24240 initEvents : function()
24242 this.el.on('mouseover', this.onMouseOver, this);
24243 this.el.on('mouseout', this.onMouseOut, this);
24245 this.el.select('a', true).first().on('click', this.onClick, this);
24249 onClick : function(e)
24251 if(this.preventDefault){
24252 e.preventDefault();
24255 this.fireEvent("click", this, e);
24258 onMouseOver : function(e)
24260 if(this.submenu && this.pos == 'left'){
24261 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24264 this.fireEvent("mouseover", this, e);
24267 onMouseOut : function(e)
24269 this.fireEvent("mouseout", this, e);
24281 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24284 * @class Roo.bootstrap.menu.Separator
24285 * @extends Roo.bootstrap.Component
24286 * Bootstrap Separator class
24289 * Create a new Separator
24290 * @param {Object} config The config object
24294 Roo.bootstrap.menu.Separator = function(config){
24295 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24298 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24300 getAutoCreate : function(){
24321 * @class Roo.bootstrap.Tooltip
24322 * Bootstrap Tooltip class
24323 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24324 * to determine which dom element triggers the tooltip.
24326 * It needs to add support for additional attributes like tooltip-position
24329 * Create a new Toolti
24330 * @param {Object} config The config object
24333 Roo.bootstrap.Tooltip = function(config){
24334 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24337 Roo.apply(Roo.bootstrap.Tooltip, {
24339 * @function init initialize tooltip monitoring.
24343 currentTip : false,
24344 currentRegion : false,
24350 Roo.get(document).on('mouseover', this.enter ,this);
24351 Roo.get(document).on('mouseout', this.leave, this);
24354 this.currentTip = new Roo.bootstrap.Tooltip();
24357 enter : function(ev)
24359 var dom = ev.getTarget();
24361 //Roo.log(['enter',dom]);
24362 var el = Roo.fly(dom);
24363 if (this.currentEl) {
24365 //Roo.log(this.currentEl);
24366 //Roo.log(this.currentEl.contains(dom));
24367 if (this.currentEl == el) {
24370 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24376 if (this.currentTip.el) {
24377 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24381 if(!el || el.dom == document){
24387 // you can not look for children, as if el is the body.. then everythign is the child..
24388 if (!el.attr('tooltip')) { //
24389 if (!el.select("[tooltip]").elements.length) {
24392 // is the mouse over this child...?
24393 bindEl = el.select("[tooltip]").first();
24394 var xy = ev.getXY();
24395 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24396 //Roo.log("not in region.");
24399 //Roo.log("child element over..");
24402 this.currentEl = bindEl;
24403 this.currentTip.bind(bindEl);
24404 this.currentRegion = Roo.lib.Region.getRegion(dom);
24405 this.currentTip.enter();
24408 leave : function(ev)
24410 var dom = ev.getTarget();
24411 //Roo.log(['leave',dom]);
24412 if (!this.currentEl) {
24417 if (dom != this.currentEl.dom) {
24420 var xy = ev.getXY();
24421 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24424 // only activate leave if mouse cursor is outside... bounding box..
24429 if (this.currentTip) {
24430 this.currentTip.leave();
24432 //Roo.log('clear currentEl');
24433 this.currentEl = false;
24438 'left' : ['r-l', [-2,0], 'right'],
24439 'right' : ['l-r', [2,0], 'left'],
24440 'bottom' : ['t-b', [0,2], 'top'],
24441 'top' : [ 'b-t', [0,-2], 'bottom']
24447 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24452 delay : null, // can be { show : 300 , hide: 500}
24456 hoverState : null, //???
24458 placement : 'bottom',
24460 getAutoCreate : function(){
24467 cls : 'tooltip-arrow'
24470 cls : 'tooltip-inner'
24477 bind : function(el)
24483 enter : function () {
24485 if (this.timeout != null) {
24486 clearTimeout(this.timeout);
24489 this.hoverState = 'in';
24490 //Roo.log("enter - show");
24491 if (!this.delay || !this.delay.show) {
24496 this.timeout = setTimeout(function () {
24497 if (_t.hoverState == 'in') {
24500 }, this.delay.show);
24504 clearTimeout(this.timeout);
24506 this.hoverState = 'out';
24507 if (!this.delay || !this.delay.hide) {
24513 this.timeout = setTimeout(function () {
24514 //Roo.log("leave - timeout");
24516 if (_t.hoverState == 'out') {
24518 Roo.bootstrap.Tooltip.currentEl = false;
24526 this.render(document.body);
24529 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24531 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24533 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24535 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24537 var placement = typeof this.placement == 'function' ?
24538 this.placement.call(this, this.el, on_el) :
24541 var autoToken = /\s?auto?\s?/i;
24542 var autoPlace = autoToken.test(placement);
24544 placement = placement.replace(autoToken, '') || 'top';
24548 //this.el.setXY([0,0]);
24550 //this.el.dom.style.display='block';
24552 //this.el.appendTo(on_el);
24554 var p = this.getPosition();
24555 var box = this.el.getBox();
24561 var align = Roo.bootstrap.Tooltip.alignment[placement];
24563 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24565 if(placement == 'top' || placement == 'bottom'){
24567 placement = 'right';
24570 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24571 placement = 'left';
24574 var scroll = Roo.select('body', true).first().getScroll();
24576 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24582 align = Roo.bootstrap.Tooltip.alignment[placement];
24584 this.el.alignTo(this.bindEl, align[0],align[1]);
24585 //var arrow = this.el.select('.arrow',true).first();
24586 //arrow.set(align[2],
24588 this.el.addClass(placement);
24590 this.el.addClass('in fade');
24592 this.hoverState = null;
24594 if (this.el.hasClass('fade')) {
24605 //this.el.setXY([0,0]);
24606 this.el.removeClass('in');
24622 * @class Roo.bootstrap.LocationPicker
24623 * @extends Roo.bootstrap.Component
24624 * Bootstrap LocationPicker class
24625 * @cfg {Number} latitude Position when init default 0
24626 * @cfg {Number} longitude Position when init default 0
24627 * @cfg {Number} zoom default 15
24628 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24629 * @cfg {Boolean} mapTypeControl default false
24630 * @cfg {Boolean} disableDoubleClickZoom default false
24631 * @cfg {Boolean} scrollwheel default true
24632 * @cfg {Boolean} streetViewControl default false
24633 * @cfg {Number} radius default 0
24634 * @cfg {String} locationName
24635 * @cfg {Boolean} draggable default true
24636 * @cfg {Boolean} enableAutocomplete default false
24637 * @cfg {Boolean} enableReverseGeocode default true
24638 * @cfg {String} markerTitle
24641 * Create a new LocationPicker
24642 * @param {Object} config The config object
24646 Roo.bootstrap.LocationPicker = function(config){
24648 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24653 * Fires when the picker initialized.
24654 * @param {Roo.bootstrap.LocationPicker} this
24655 * @param {Google Location} location
24659 * @event positionchanged
24660 * Fires when the picker position changed.
24661 * @param {Roo.bootstrap.LocationPicker} this
24662 * @param {Google Location} location
24664 positionchanged : true,
24667 * Fires when the map resize.
24668 * @param {Roo.bootstrap.LocationPicker} this
24673 * Fires when the map show.
24674 * @param {Roo.bootstrap.LocationPicker} this
24679 * Fires when the map hide.
24680 * @param {Roo.bootstrap.LocationPicker} this
24685 * Fires when click the map.
24686 * @param {Roo.bootstrap.LocationPicker} this
24687 * @param {Map event} e
24691 * @event mapRightClick
24692 * Fires when right click the map.
24693 * @param {Roo.bootstrap.LocationPicker} this
24694 * @param {Map event} e
24696 mapRightClick : true,
24698 * @event markerClick
24699 * Fires when click the marker.
24700 * @param {Roo.bootstrap.LocationPicker} this
24701 * @param {Map event} e
24703 markerClick : true,
24705 * @event markerRightClick
24706 * Fires when right click the marker.
24707 * @param {Roo.bootstrap.LocationPicker} this
24708 * @param {Map event} e
24710 markerRightClick : true,
24712 * @event OverlayViewDraw
24713 * Fires when OverlayView Draw
24714 * @param {Roo.bootstrap.LocationPicker} this
24716 OverlayViewDraw : true,
24718 * @event OverlayViewOnAdd
24719 * Fires when OverlayView Draw
24720 * @param {Roo.bootstrap.LocationPicker} this
24722 OverlayViewOnAdd : true,
24724 * @event OverlayViewOnRemove
24725 * Fires when OverlayView Draw
24726 * @param {Roo.bootstrap.LocationPicker} this
24728 OverlayViewOnRemove : true,
24730 * @event OverlayViewShow
24731 * Fires when OverlayView Draw
24732 * @param {Roo.bootstrap.LocationPicker} this
24733 * @param {Pixel} cpx
24735 OverlayViewShow : true,
24737 * @event OverlayViewHide
24738 * Fires when OverlayView Draw
24739 * @param {Roo.bootstrap.LocationPicker} this
24741 OverlayViewHide : true,
24743 * @event loadexception
24744 * Fires when load google lib failed.
24745 * @param {Roo.bootstrap.LocationPicker} this
24747 loadexception : true
24752 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24754 gMapContext: false,
24760 mapTypeControl: false,
24761 disableDoubleClickZoom: false,
24763 streetViewControl: false,
24767 enableAutocomplete: false,
24768 enableReverseGeocode: true,
24771 getAutoCreate: function()
24776 cls: 'roo-location-picker'
24782 initEvents: function(ct, position)
24784 if(!this.el.getWidth() || this.isApplied()){
24788 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24793 initial: function()
24795 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24796 this.fireEvent('loadexception', this);
24800 if(!this.mapTypeId){
24801 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24804 this.gMapContext = this.GMapContext();
24806 this.initOverlayView();
24808 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24812 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24813 _this.setPosition(_this.gMapContext.marker.position);
24816 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24817 _this.fireEvent('mapClick', this, event);
24821 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24822 _this.fireEvent('mapRightClick', this, event);
24826 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24827 _this.fireEvent('markerClick', this, event);
24831 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24832 _this.fireEvent('markerRightClick', this, event);
24836 this.setPosition(this.gMapContext.location);
24838 this.fireEvent('initial', this, this.gMapContext.location);
24841 initOverlayView: function()
24845 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24849 _this.fireEvent('OverlayViewDraw', _this);
24854 _this.fireEvent('OverlayViewOnAdd', _this);
24857 onRemove: function()
24859 _this.fireEvent('OverlayViewOnRemove', _this);
24862 show: function(cpx)
24864 _this.fireEvent('OverlayViewShow', _this, cpx);
24869 _this.fireEvent('OverlayViewHide', _this);
24875 fromLatLngToContainerPixel: function(event)
24877 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24880 isApplied: function()
24882 return this.getGmapContext() == false ? false : true;
24885 getGmapContext: function()
24887 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24890 GMapContext: function()
24892 var position = new google.maps.LatLng(this.latitude, this.longitude);
24894 var _map = new google.maps.Map(this.el.dom, {
24897 mapTypeId: this.mapTypeId,
24898 mapTypeControl: this.mapTypeControl,
24899 disableDoubleClickZoom: this.disableDoubleClickZoom,
24900 scrollwheel: this.scrollwheel,
24901 streetViewControl: this.streetViewControl,
24902 locationName: this.locationName,
24903 draggable: this.draggable,
24904 enableAutocomplete: this.enableAutocomplete,
24905 enableReverseGeocode: this.enableReverseGeocode
24908 var _marker = new google.maps.Marker({
24909 position: position,
24911 title: this.markerTitle,
24912 draggable: this.draggable
24919 location: position,
24920 radius: this.radius,
24921 locationName: this.locationName,
24922 addressComponents: {
24923 formatted_address: null,
24924 addressLine1: null,
24925 addressLine2: null,
24927 streetNumber: null,
24931 stateOrProvince: null
24934 domContainer: this.el.dom,
24935 geodecoder: new google.maps.Geocoder()
24939 drawCircle: function(center, radius, options)
24941 if (this.gMapContext.circle != null) {
24942 this.gMapContext.circle.setMap(null);
24946 options = Roo.apply({}, options, {
24947 strokeColor: "#0000FF",
24948 strokeOpacity: .35,
24950 fillColor: "#0000FF",
24954 options.map = this.gMapContext.map;
24955 options.radius = radius;
24956 options.center = center;
24957 this.gMapContext.circle = new google.maps.Circle(options);
24958 return this.gMapContext.circle;
24964 setPosition: function(location)
24966 this.gMapContext.location = location;
24967 this.gMapContext.marker.setPosition(location);
24968 this.gMapContext.map.panTo(location);
24969 this.drawCircle(location, this.gMapContext.radius, {});
24973 if (this.gMapContext.settings.enableReverseGeocode) {
24974 this.gMapContext.geodecoder.geocode({
24975 latLng: this.gMapContext.location
24976 }, function(results, status) {
24978 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24979 _this.gMapContext.locationName = results[0].formatted_address;
24980 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24982 _this.fireEvent('positionchanged', this, location);
24989 this.fireEvent('positionchanged', this, location);
24994 google.maps.event.trigger(this.gMapContext.map, "resize");
24996 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24998 this.fireEvent('resize', this);
25001 setPositionByLatLng: function(latitude, longitude)
25003 this.setPosition(new google.maps.LatLng(latitude, longitude));
25006 getCurrentPosition: function()
25009 latitude: this.gMapContext.location.lat(),
25010 longitude: this.gMapContext.location.lng()
25014 getAddressName: function()
25016 return this.gMapContext.locationName;
25019 getAddressComponents: function()
25021 return this.gMapContext.addressComponents;
25024 address_component_from_google_geocode: function(address_components)
25028 for (var i = 0; i < address_components.length; i++) {
25029 var component = address_components[i];
25030 if (component.types.indexOf("postal_code") >= 0) {
25031 result.postalCode = component.short_name;
25032 } else if (component.types.indexOf("street_number") >= 0) {
25033 result.streetNumber = component.short_name;
25034 } else if (component.types.indexOf("route") >= 0) {
25035 result.streetName = component.short_name;
25036 } else if (component.types.indexOf("neighborhood") >= 0) {
25037 result.city = component.short_name;
25038 } else if (component.types.indexOf("locality") >= 0) {
25039 result.city = component.short_name;
25040 } else if (component.types.indexOf("sublocality") >= 0) {
25041 result.district = component.short_name;
25042 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25043 result.stateOrProvince = component.short_name;
25044 } else if (component.types.indexOf("country") >= 0) {
25045 result.country = component.short_name;
25049 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25050 result.addressLine2 = "";
25054 setZoomLevel: function(zoom)
25056 this.gMapContext.map.setZoom(zoom);
25069 this.fireEvent('show', this);
25080 this.fireEvent('hide', this);
25085 Roo.apply(Roo.bootstrap.LocationPicker, {
25087 OverlayView : function(map, options)
25089 options = options || {};
25103 * @class Roo.bootstrap.Alert
25104 * @extends Roo.bootstrap.Component
25105 * Bootstrap Alert class
25106 * @cfg {String} title The title of alert
25107 * @cfg {String} html The content of alert
25108 * @cfg {String} weight ( success | info | warning | danger )
25109 * @cfg {String} faicon font-awesomeicon
25112 * Create a new alert
25113 * @param {Object} config The config object
25117 Roo.bootstrap.Alert = function(config){
25118 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25122 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25129 getAutoCreate : function()
25138 cls : 'roo-alert-icon'
25143 cls : 'roo-alert-title',
25148 cls : 'roo-alert-text',
25155 cfg.cn[0].cls += ' fa ' + this.faicon;
25159 cfg.cls += ' alert-' + this.weight;
25165 initEvents: function()
25167 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25170 setTitle : function(str)
25172 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25175 setText : function(str)
25177 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25180 setWeight : function(weight)
25183 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25186 this.weight = weight;
25188 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25191 setIcon : function(icon)
25194 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25197 this.faicon = icon;
25199 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25220 * @class Roo.bootstrap.UploadCropbox
25221 * @extends Roo.bootstrap.Component
25222 * Bootstrap UploadCropbox class
25223 * @cfg {String} emptyText show when image has been loaded
25224 * @cfg {String} rotateNotify show when image too small to rotate
25225 * @cfg {Number} errorTimeout default 3000
25226 * @cfg {Number} minWidth default 300
25227 * @cfg {Number} minHeight default 300
25228 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25229 * @cfg {Boolean} isDocument (true|false) default false
25230 * @cfg {String} url action url
25231 * @cfg {String} paramName default 'imageUpload'
25232 * @cfg {String} method default POST
25233 * @cfg {Boolean} loadMask (true|false) default true
25234 * @cfg {Boolean} loadingText default 'Loading...'
25237 * Create a new UploadCropbox
25238 * @param {Object} config The config object
25241 Roo.bootstrap.UploadCropbox = function(config){
25242 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25246 * @event beforeselectfile
25247 * Fire before select file
25248 * @param {Roo.bootstrap.UploadCropbox} this
25250 "beforeselectfile" : true,
25253 * Fire after initEvent
25254 * @param {Roo.bootstrap.UploadCropbox} this
25259 * Fire after initEvent
25260 * @param {Roo.bootstrap.UploadCropbox} this
25261 * @param {String} data
25266 * Fire when preparing the file data
25267 * @param {Roo.bootstrap.UploadCropbox} this
25268 * @param {Object} file
25273 * Fire when get exception
25274 * @param {Roo.bootstrap.UploadCropbox} this
25275 * @param {XMLHttpRequest} xhr
25277 "exception" : true,
25279 * @event beforeloadcanvas
25280 * Fire before load the canvas
25281 * @param {Roo.bootstrap.UploadCropbox} this
25282 * @param {String} src
25284 "beforeloadcanvas" : true,
25287 * Fire when trash image
25288 * @param {Roo.bootstrap.UploadCropbox} this
25293 * Fire when download the image
25294 * @param {Roo.bootstrap.UploadCropbox} this
25298 * @event footerbuttonclick
25299 * Fire when footerbuttonclick
25300 * @param {Roo.bootstrap.UploadCropbox} this
25301 * @param {String} type
25303 "footerbuttonclick" : true,
25307 * @param {Roo.bootstrap.UploadCropbox} this
25312 * Fire when rotate the image
25313 * @param {Roo.bootstrap.UploadCropbox} this
25314 * @param {String} pos
25319 * Fire when inspect the file
25320 * @param {Roo.bootstrap.UploadCropbox} this
25321 * @param {Object} file
25326 * Fire when xhr upload the file
25327 * @param {Roo.bootstrap.UploadCropbox} this
25328 * @param {Object} data
25333 * Fire when arrange the file data
25334 * @param {Roo.bootstrap.UploadCropbox} this
25335 * @param {Object} formData
25340 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25343 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25345 emptyText : 'Click to upload image',
25346 rotateNotify : 'Image is too small to rotate',
25347 errorTimeout : 3000,
25361 cropType : 'image/jpeg',
25363 canvasLoaded : false,
25364 isDocument : false,
25366 paramName : 'imageUpload',
25368 loadingText : 'Loading...',
25371 getAutoCreate : function()
25375 cls : 'roo-upload-cropbox',
25379 cls : 'roo-upload-cropbox-selector',
25384 cls : 'roo-upload-cropbox-body',
25385 style : 'cursor:pointer',
25389 cls : 'roo-upload-cropbox-preview'
25393 cls : 'roo-upload-cropbox-thumb'
25397 cls : 'roo-upload-cropbox-empty-notify',
25398 html : this.emptyText
25402 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25403 html : this.rotateNotify
25409 cls : 'roo-upload-cropbox-footer',
25412 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25422 onRender : function(ct, position)
25424 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25426 if (this.buttons.length) {
25428 Roo.each(this.buttons, function(bb) {
25430 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25432 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25438 this.maskEl = this.el;
25442 initEvents : function()
25444 this.urlAPI = (window.createObjectURL && window) ||
25445 (window.URL && URL.revokeObjectURL && URL) ||
25446 (window.webkitURL && webkitURL);
25448 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25449 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25451 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25452 this.selectorEl.hide();
25454 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25455 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25457 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25458 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25459 this.thumbEl.hide();
25461 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25462 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25464 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25465 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25466 this.errorEl.hide();
25468 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25469 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25470 this.footerEl.hide();
25472 this.setThumbBoxSize();
25478 this.fireEvent('initial', this);
25485 window.addEventListener("resize", function() { _this.resize(); } );
25487 this.bodyEl.on('click', this.beforeSelectFile, this);
25490 this.bodyEl.on('touchstart', this.onTouchStart, this);
25491 this.bodyEl.on('touchmove', this.onTouchMove, this);
25492 this.bodyEl.on('touchend', this.onTouchEnd, this);
25496 this.bodyEl.on('mousedown', this.onMouseDown, this);
25497 this.bodyEl.on('mousemove', this.onMouseMove, this);
25498 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25499 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25500 Roo.get(document).on('mouseup', this.onMouseUp, this);
25503 this.selectorEl.on('change', this.onFileSelected, this);
25509 this.baseScale = 1;
25511 this.baseRotate = 1;
25512 this.dragable = false;
25513 this.pinching = false;
25516 this.cropData = false;
25517 this.notifyEl.dom.innerHTML = this.emptyText;
25519 this.selectorEl.dom.value = '';
25523 resize : function()
25525 if(this.fireEvent('resize', this) != false){
25526 this.setThumbBoxPosition();
25527 this.setCanvasPosition();
25531 onFooterButtonClick : function(e, el, o, type)
25534 case 'rotate-left' :
25535 this.onRotateLeft(e);
25537 case 'rotate-right' :
25538 this.onRotateRight(e);
25541 this.beforeSelectFile(e);
25556 this.fireEvent('footerbuttonclick', this, type);
25559 beforeSelectFile : function(e)
25561 e.preventDefault();
25563 if(this.fireEvent('beforeselectfile', this) != false){
25564 this.selectorEl.dom.click();
25568 onFileSelected : function(e)
25570 e.preventDefault();
25572 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25576 var file = this.selectorEl.dom.files[0];
25578 if(this.fireEvent('inspect', this, file) != false){
25579 this.prepare(file);
25584 trash : function(e)
25586 this.fireEvent('trash', this);
25589 download : function(e)
25591 this.fireEvent('download', this);
25594 loadCanvas : function(src)
25596 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25600 this.imageEl = document.createElement('img');
25604 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25606 this.imageEl.src = src;
25610 onLoadCanvas : function()
25612 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25613 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25615 this.bodyEl.un('click', this.beforeSelectFile, this);
25617 this.notifyEl.hide();
25618 this.thumbEl.show();
25619 this.footerEl.show();
25621 this.baseRotateLevel();
25623 if(this.isDocument){
25624 this.setThumbBoxSize();
25627 this.setThumbBoxPosition();
25629 this.baseScaleLevel();
25635 this.canvasLoaded = true;
25638 this.maskEl.unmask();
25643 setCanvasPosition : function()
25645 if(!this.canvasEl){
25649 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25650 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25652 this.previewEl.setLeft(pw);
25653 this.previewEl.setTop(ph);
25657 onMouseDown : function(e)
25661 this.dragable = true;
25662 this.pinching = false;
25664 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25665 this.dragable = false;
25669 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25670 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25674 onMouseMove : function(e)
25678 if(!this.canvasLoaded){
25682 if (!this.dragable){
25686 var minX = Math.ceil(this.thumbEl.getLeft(true));
25687 var minY = Math.ceil(this.thumbEl.getTop(true));
25689 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25690 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25692 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25693 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25695 x = x - this.mouseX;
25696 y = y - this.mouseY;
25698 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25699 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25701 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25702 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25704 this.previewEl.setLeft(bgX);
25705 this.previewEl.setTop(bgY);
25707 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25708 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25711 onMouseUp : function(e)
25715 this.dragable = false;
25718 onMouseWheel : function(e)
25722 this.startScale = this.scale;
25724 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25726 if(!this.zoomable()){
25727 this.scale = this.startScale;
25736 zoomable : function()
25738 var minScale = this.thumbEl.getWidth() / this.minWidth;
25740 if(this.minWidth < this.minHeight){
25741 minScale = this.thumbEl.getHeight() / this.minHeight;
25744 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25745 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25749 (this.rotate == 0 || this.rotate == 180) &&
25751 width > this.imageEl.OriginWidth ||
25752 height > this.imageEl.OriginHeight ||
25753 (width < this.minWidth && height < this.minHeight)
25761 (this.rotate == 90 || this.rotate == 270) &&
25763 width > this.imageEl.OriginWidth ||
25764 height > this.imageEl.OriginHeight ||
25765 (width < this.minHeight && height < this.minWidth)
25772 !this.isDocument &&
25773 (this.rotate == 0 || this.rotate == 180) &&
25775 width < this.minWidth ||
25776 width > this.imageEl.OriginWidth ||
25777 height < this.minHeight ||
25778 height > this.imageEl.OriginHeight
25785 !this.isDocument &&
25786 (this.rotate == 90 || this.rotate == 270) &&
25788 width < this.minHeight ||
25789 width > this.imageEl.OriginWidth ||
25790 height < this.minWidth ||
25791 height > this.imageEl.OriginHeight
25801 onRotateLeft : function(e)
25803 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25805 var minScale = this.thumbEl.getWidth() / this.minWidth;
25807 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25808 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25810 this.startScale = this.scale;
25812 while (this.getScaleLevel() < minScale){
25814 this.scale = this.scale + 1;
25816 if(!this.zoomable()){
25821 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25822 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25827 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25834 this.scale = this.startScale;
25836 this.onRotateFail();
25841 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25843 if(this.isDocument){
25844 this.setThumbBoxSize();
25845 this.setThumbBoxPosition();
25846 this.setCanvasPosition();
25851 this.fireEvent('rotate', this, 'left');
25855 onRotateRight : function(e)
25857 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25859 var minScale = this.thumbEl.getWidth() / this.minWidth;
25861 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25862 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25864 this.startScale = this.scale;
25866 while (this.getScaleLevel() < minScale){
25868 this.scale = this.scale + 1;
25870 if(!this.zoomable()){
25875 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25876 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25881 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25888 this.scale = this.startScale;
25890 this.onRotateFail();
25895 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25897 if(this.isDocument){
25898 this.setThumbBoxSize();
25899 this.setThumbBoxPosition();
25900 this.setCanvasPosition();
25905 this.fireEvent('rotate', this, 'right');
25908 onRotateFail : function()
25910 this.errorEl.show(true);
25914 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25919 this.previewEl.dom.innerHTML = '';
25921 var canvasEl = document.createElement("canvas");
25923 var contextEl = canvasEl.getContext("2d");
25925 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25926 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25927 var center = this.imageEl.OriginWidth / 2;
25929 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25930 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25931 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25932 center = this.imageEl.OriginHeight / 2;
25935 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25937 contextEl.translate(center, center);
25938 contextEl.rotate(this.rotate * Math.PI / 180);
25940 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25942 this.canvasEl = document.createElement("canvas");
25944 this.contextEl = this.canvasEl.getContext("2d");
25946 switch (this.rotate) {
25949 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25950 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25952 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25957 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25958 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25960 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25961 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);
25965 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25970 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25971 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25973 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25974 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);
25978 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);
25983 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25984 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25986 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25987 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25991 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);
25998 this.previewEl.appendChild(this.canvasEl);
26000 this.setCanvasPosition();
26005 if(!this.canvasLoaded){
26009 var imageCanvas = document.createElement("canvas");
26011 var imageContext = imageCanvas.getContext("2d");
26013 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26014 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26016 var center = imageCanvas.width / 2;
26018 imageContext.translate(center, center);
26020 imageContext.rotate(this.rotate * Math.PI / 180);
26022 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26024 var canvas = document.createElement("canvas");
26026 var context = canvas.getContext("2d");
26028 canvas.width = this.minWidth;
26029 canvas.height = this.minHeight;
26031 switch (this.rotate) {
26034 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26035 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26037 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26038 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26040 var targetWidth = this.minWidth - 2 * x;
26041 var targetHeight = this.minHeight - 2 * y;
26045 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26046 scale = targetWidth / width;
26049 if(x > 0 && y == 0){
26050 scale = targetHeight / height;
26053 if(x > 0 && y > 0){
26054 scale = targetWidth / width;
26056 if(width < height){
26057 scale = targetHeight / height;
26061 context.scale(scale, scale);
26063 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26064 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26066 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26067 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26069 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26074 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26075 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26077 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26078 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26080 var targetWidth = this.minWidth - 2 * x;
26081 var targetHeight = this.minHeight - 2 * y;
26085 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26086 scale = targetWidth / width;
26089 if(x > 0 && y == 0){
26090 scale = targetHeight / height;
26093 if(x > 0 && y > 0){
26094 scale = targetWidth / width;
26096 if(width < height){
26097 scale = targetHeight / height;
26101 context.scale(scale, scale);
26103 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26104 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26106 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26107 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26109 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26111 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26116 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26117 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26119 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26120 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26122 var targetWidth = this.minWidth - 2 * x;
26123 var targetHeight = this.minHeight - 2 * y;
26127 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26128 scale = targetWidth / width;
26131 if(x > 0 && y == 0){
26132 scale = targetHeight / height;
26135 if(x > 0 && y > 0){
26136 scale = targetWidth / width;
26138 if(width < height){
26139 scale = targetHeight / height;
26143 context.scale(scale, scale);
26145 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26146 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26148 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26149 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26151 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26152 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26154 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26159 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26160 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26162 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26163 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26165 var targetWidth = this.minWidth - 2 * x;
26166 var targetHeight = this.minHeight - 2 * y;
26170 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26171 scale = targetWidth / width;
26174 if(x > 0 && y == 0){
26175 scale = targetHeight / height;
26178 if(x > 0 && y > 0){
26179 scale = targetWidth / width;
26181 if(width < height){
26182 scale = targetHeight / height;
26186 context.scale(scale, scale);
26188 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26189 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26191 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26192 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26194 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26196 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26203 this.cropData = canvas.toDataURL(this.cropType);
26205 if(this.fireEvent('crop', this, this.cropData) !== false){
26206 this.process(this.file, this.cropData);
26213 setThumbBoxSize : function()
26217 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26218 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26219 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26221 this.minWidth = width;
26222 this.minHeight = height;
26224 if(this.rotate == 90 || this.rotate == 270){
26225 this.minWidth = height;
26226 this.minHeight = width;
26231 width = Math.ceil(this.minWidth * height / this.minHeight);
26233 if(this.minWidth > this.minHeight){
26235 height = Math.ceil(this.minHeight * width / this.minWidth);
26238 this.thumbEl.setStyle({
26239 width : width + 'px',
26240 height : height + 'px'
26247 setThumbBoxPosition : function()
26249 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26250 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26252 this.thumbEl.setLeft(x);
26253 this.thumbEl.setTop(y);
26257 baseRotateLevel : function()
26259 this.baseRotate = 1;
26262 typeof(this.exif) != 'undefined' &&
26263 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26264 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26266 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26269 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26273 baseScaleLevel : function()
26277 if(this.isDocument){
26279 if(this.baseRotate == 6 || this.baseRotate == 8){
26281 height = this.thumbEl.getHeight();
26282 this.baseScale = height / this.imageEl.OriginWidth;
26284 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26285 width = this.thumbEl.getWidth();
26286 this.baseScale = width / this.imageEl.OriginHeight;
26292 height = this.thumbEl.getHeight();
26293 this.baseScale = height / this.imageEl.OriginHeight;
26295 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26296 width = this.thumbEl.getWidth();
26297 this.baseScale = width / this.imageEl.OriginWidth;
26303 if(this.baseRotate == 6 || this.baseRotate == 8){
26305 width = this.thumbEl.getHeight();
26306 this.baseScale = width / this.imageEl.OriginHeight;
26308 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26309 height = this.thumbEl.getWidth();
26310 this.baseScale = height / this.imageEl.OriginHeight;
26313 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26314 height = this.thumbEl.getWidth();
26315 this.baseScale = height / this.imageEl.OriginHeight;
26317 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26318 width = this.thumbEl.getHeight();
26319 this.baseScale = width / this.imageEl.OriginWidth;
26326 width = this.thumbEl.getWidth();
26327 this.baseScale = width / this.imageEl.OriginWidth;
26329 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26330 height = this.thumbEl.getHeight();
26331 this.baseScale = height / this.imageEl.OriginHeight;
26334 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26336 height = this.thumbEl.getHeight();
26337 this.baseScale = height / this.imageEl.OriginHeight;
26339 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26340 width = this.thumbEl.getWidth();
26341 this.baseScale = width / this.imageEl.OriginWidth;
26349 getScaleLevel : function()
26351 return this.baseScale * Math.pow(1.1, this.scale);
26354 onTouchStart : function(e)
26356 if(!this.canvasLoaded){
26357 this.beforeSelectFile(e);
26361 var touches = e.browserEvent.touches;
26367 if(touches.length == 1){
26368 this.onMouseDown(e);
26372 if(touches.length != 2){
26378 for(var i = 0, finger; finger = touches[i]; i++){
26379 coords.push(finger.pageX, finger.pageY);
26382 var x = Math.pow(coords[0] - coords[2], 2);
26383 var y = Math.pow(coords[1] - coords[3], 2);
26385 this.startDistance = Math.sqrt(x + y);
26387 this.startScale = this.scale;
26389 this.pinching = true;
26390 this.dragable = false;
26394 onTouchMove : function(e)
26396 if(!this.pinching && !this.dragable){
26400 var touches = e.browserEvent.touches;
26407 this.onMouseMove(e);
26413 for(var i = 0, finger; finger = touches[i]; i++){
26414 coords.push(finger.pageX, finger.pageY);
26417 var x = Math.pow(coords[0] - coords[2], 2);
26418 var y = Math.pow(coords[1] - coords[3], 2);
26420 this.endDistance = Math.sqrt(x + y);
26422 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26424 if(!this.zoomable()){
26425 this.scale = this.startScale;
26433 onTouchEnd : function(e)
26435 this.pinching = false;
26436 this.dragable = false;
26440 process : function(file, crop)
26443 this.maskEl.mask(this.loadingText);
26446 this.xhr = new XMLHttpRequest();
26448 file.xhr = this.xhr;
26450 this.xhr.open(this.method, this.url, true);
26453 "Accept": "application/json",
26454 "Cache-Control": "no-cache",
26455 "X-Requested-With": "XMLHttpRequest"
26458 for (var headerName in headers) {
26459 var headerValue = headers[headerName];
26461 this.xhr.setRequestHeader(headerName, headerValue);
26467 this.xhr.onload = function()
26469 _this.xhrOnLoad(_this.xhr);
26472 this.xhr.onerror = function()
26474 _this.xhrOnError(_this.xhr);
26477 var formData = new FormData();
26479 formData.append('returnHTML', 'NO');
26482 formData.append('crop', crop);
26485 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26486 formData.append(this.paramName, file, file.name);
26489 if(typeof(file.filename) != 'undefined'){
26490 formData.append('filename', file.filename);
26493 if(typeof(file.mimetype) != 'undefined'){
26494 formData.append('mimetype', file.mimetype);
26497 if(this.fireEvent('arrange', this, formData) != false){
26498 this.xhr.send(formData);
26502 xhrOnLoad : function(xhr)
26505 this.maskEl.unmask();
26508 if (xhr.readyState !== 4) {
26509 this.fireEvent('exception', this, xhr);
26513 var response = Roo.decode(xhr.responseText);
26515 if(!response.success){
26516 this.fireEvent('exception', this, xhr);
26520 var response = Roo.decode(xhr.responseText);
26522 this.fireEvent('upload', this, response);
26526 xhrOnError : function()
26529 this.maskEl.unmask();
26532 Roo.log('xhr on error');
26534 var response = Roo.decode(xhr.responseText);
26540 prepare : function(file)
26543 this.maskEl.mask(this.loadingText);
26549 if(typeof(file) === 'string'){
26550 this.loadCanvas(file);
26554 if(!file || !this.urlAPI){
26559 this.cropType = file.type;
26563 if(this.fireEvent('prepare', this, this.file) != false){
26565 var reader = new FileReader();
26567 reader.onload = function (e) {
26568 if (e.target.error) {
26569 Roo.log(e.target.error);
26573 var buffer = e.target.result,
26574 dataView = new DataView(buffer),
26576 maxOffset = dataView.byteLength - 4,
26580 if (dataView.getUint16(0) === 0xffd8) {
26581 while (offset < maxOffset) {
26582 markerBytes = dataView.getUint16(offset);
26584 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26585 markerLength = dataView.getUint16(offset + 2) + 2;
26586 if (offset + markerLength > dataView.byteLength) {
26587 Roo.log('Invalid meta data: Invalid segment size.');
26591 if(markerBytes == 0xffe1){
26592 _this.parseExifData(
26599 offset += markerLength;
26609 var url = _this.urlAPI.createObjectURL(_this.file);
26611 _this.loadCanvas(url);
26616 reader.readAsArrayBuffer(this.file);
26622 parseExifData : function(dataView, offset, length)
26624 var tiffOffset = offset + 10,
26628 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26629 // No Exif data, might be XMP data instead
26633 // Check for the ASCII code for "Exif" (0x45786966):
26634 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26635 // No Exif data, might be XMP data instead
26638 if (tiffOffset + 8 > dataView.byteLength) {
26639 Roo.log('Invalid Exif data: Invalid segment size.');
26642 // Check for the two null bytes:
26643 if (dataView.getUint16(offset + 8) !== 0x0000) {
26644 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26647 // Check the byte alignment:
26648 switch (dataView.getUint16(tiffOffset)) {
26650 littleEndian = true;
26653 littleEndian = false;
26656 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26659 // Check for the TIFF tag marker (0x002A):
26660 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26661 Roo.log('Invalid Exif data: Missing TIFF marker.');
26664 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26665 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26667 this.parseExifTags(
26670 tiffOffset + dirOffset,
26675 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26680 if (dirOffset + 6 > dataView.byteLength) {
26681 Roo.log('Invalid Exif data: Invalid directory offset.');
26684 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26685 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26686 if (dirEndOffset + 4 > dataView.byteLength) {
26687 Roo.log('Invalid Exif data: Invalid directory size.');
26690 for (i = 0; i < tagsNumber; i += 1) {
26694 dirOffset + 2 + 12 * i, // tag offset
26698 // Return the offset to the next directory:
26699 return dataView.getUint32(dirEndOffset, littleEndian);
26702 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26704 var tag = dataView.getUint16(offset, littleEndian);
26706 this.exif[tag] = this.getExifValue(
26710 dataView.getUint16(offset + 2, littleEndian), // tag type
26711 dataView.getUint32(offset + 4, littleEndian), // tag length
26716 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26718 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26727 Roo.log('Invalid Exif data: Invalid tag type.');
26731 tagSize = tagType.size * length;
26732 // Determine if the value is contained in the dataOffset bytes,
26733 // or if the value at the dataOffset is a pointer to the actual data:
26734 dataOffset = tagSize > 4 ?
26735 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26736 if (dataOffset + tagSize > dataView.byteLength) {
26737 Roo.log('Invalid Exif data: Invalid data offset.');
26740 if (length === 1) {
26741 return tagType.getValue(dataView, dataOffset, littleEndian);
26744 for (i = 0; i < length; i += 1) {
26745 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26748 if (tagType.ascii) {
26750 // Concatenate the chars:
26751 for (i = 0; i < values.length; i += 1) {
26753 // Ignore the terminating NULL byte(s):
26754 if (c === '\u0000') {
26766 Roo.apply(Roo.bootstrap.UploadCropbox, {
26768 'Orientation': 0x0112
26772 1: 0, //'top-left',
26774 3: 180, //'bottom-right',
26775 // 4: 'bottom-left',
26777 6: 90, //'right-top',
26778 // 7: 'right-bottom',
26779 8: 270 //'left-bottom'
26783 // byte, 8-bit unsigned int:
26785 getValue: function (dataView, dataOffset) {
26786 return dataView.getUint8(dataOffset);
26790 // ascii, 8-bit byte:
26792 getValue: function (dataView, dataOffset) {
26793 return String.fromCharCode(dataView.getUint8(dataOffset));
26798 // short, 16 bit int:
26800 getValue: function (dataView, dataOffset, littleEndian) {
26801 return dataView.getUint16(dataOffset, littleEndian);
26805 // long, 32 bit int:
26807 getValue: function (dataView, dataOffset, littleEndian) {
26808 return dataView.getUint32(dataOffset, littleEndian);
26812 // rational = two long values, first is numerator, second is denominator:
26814 getValue: function (dataView, dataOffset, littleEndian) {
26815 return dataView.getUint32(dataOffset, littleEndian) /
26816 dataView.getUint32(dataOffset + 4, littleEndian);
26820 // slong, 32 bit signed int:
26822 getValue: function (dataView, dataOffset, littleEndian) {
26823 return dataView.getInt32(dataOffset, littleEndian);
26827 // srational, two slongs, first is numerator, second is denominator:
26829 getValue: function (dataView, dataOffset, littleEndian) {
26830 return dataView.getInt32(dataOffset, littleEndian) /
26831 dataView.getInt32(dataOffset + 4, littleEndian);
26841 cls : 'btn-group roo-upload-cropbox-rotate-left',
26842 action : 'rotate-left',
26846 cls : 'btn btn-default',
26847 html : '<i class="fa fa-undo"></i>'
26853 cls : 'btn-group roo-upload-cropbox-picture',
26854 action : 'picture',
26858 cls : 'btn btn-default',
26859 html : '<i class="fa fa-picture-o"></i>'
26865 cls : 'btn-group roo-upload-cropbox-rotate-right',
26866 action : 'rotate-right',
26870 cls : 'btn btn-default',
26871 html : '<i class="fa fa-repeat"></i>'
26879 cls : 'btn-group roo-upload-cropbox-rotate-left',
26880 action : 'rotate-left',
26884 cls : 'btn btn-default',
26885 html : '<i class="fa fa-undo"></i>'
26891 cls : 'btn-group roo-upload-cropbox-download',
26892 action : 'download',
26896 cls : 'btn btn-default',
26897 html : '<i class="fa fa-download"></i>'
26903 cls : 'btn-group roo-upload-cropbox-crop',
26908 cls : 'btn btn-default',
26909 html : '<i class="fa fa-crop"></i>'
26915 cls : 'btn-group roo-upload-cropbox-trash',
26920 cls : 'btn btn-default',
26921 html : '<i class="fa fa-trash"></i>'
26927 cls : 'btn-group roo-upload-cropbox-rotate-right',
26928 action : 'rotate-right',
26932 cls : 'btn btn-default',
26933 html : '<i class="fa fa-repeat"></i>'
26941 cls : 'btn-group roo-upload-cropbox-rotate-left',
26942 action : 'rotate-left',
26946 cls : 'btn btn-default',
26947 html : '<i class="fa fa-undo"></i>'
26953 cls : 'btn-group roo-upload-cropbox-rotate-right',
26954 action : 'rotate-right',
26958 cls : 'btn btn-default',
26959 html : '<i class="fa fa-repeat"></i>'
26972 * @class Roo.bootstrap.DocumentManager
26973 * @extends Roo.bootstrap.Component
26974 * Bootstrap DocumentManager class
26975 * @cfg {String} paramName default 'imageUpload'
26976 * @cfg {String} method default POST
26977 * @cfg {String} url action url
26978 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26979 * @cfg {Boolean} multiple multiple upload default true
26980 * @cfg {Number} thumbSize default 300
26981 * @cfg {String} fieldLabel
26982 * @cfg {Number} labelWidth default 4
26983 * @cfg {String} labelAlign (left|top) default left
26984 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26987 * Create a new DocumentManager
26988 * @param {Object} config The config object
26991 Roo.bootstrap.DocumentManager = function(config){
26992 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26997 * Fire when initial the DocumentManager
26998 * @param {Roo.bootstrap.DocumentManager} this
27003 * inspect selected file
27004 * @param {Roo.bootstrap.DocumentManager} this
27005 * @param {File} file
27010 * Fire when xhr load exception
27011 * @param {Roo.bootstrap.DocumentManager} this
27012 * @param {XMLHttpRequest} xhr
27014 "exception" : true,
27017 * prepare the form data
27018 * @param {Roo.bootstrap.DocumentManager} this
27019 * @param {Object} formData
27024 * Fire when remove the file
27025 * @param {Roo.bootstrap.DocumentManager} this
27026 * @param {Object} file
27031 * Fire after refresh the file
27032 * @param {Roo.bootstrap.DocumentManager} this
27037 * Fire after click the image
27038 * @param {Roo.bootstrap.DocumentManager} this
27039 * @param {Object} file
27044 * Fire when upload a image and editable set to true
27045 * @param {Roo.bootstrap.DocumentManager} this
27046 * @param {Object} file
27050 * @event beforeselectfile
27051 * Fire before select file
27052 * @param {Roo.bootstrap.DocumentManager} this
27054 "beforeselectfile" : true,
27057 * Fire before process file
27058 * @param {Roo.bootstrap.DocumentManager} this
27059 * @param {Object} file
27066 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27075 paramName : 'imageUpload',
27078 labelAlign : 'left',
27085 getAutoCreate : function()
27087 var managerWidget = {
27089 cls : 'roo-document-manager',
27093 cls : 'roo-document-manager-selector',
27098 cls : 'roo-document-manager-uploader',
27102 cls : 'roo-document-manager-upload-btn',
27103 html : '<i class="fa fa-plus"></i>'
27114 cls : 'column col-md-12',
27119 if(this.fieldLabel.length){
27124 cls : 'column col-md-12',
27125 html : this.fieldLabel
27129 cls : 'column col-md-12',
27134 if(this.labelAlign == 'left'){
27138 cls : 'column col-md-' + this.labelWidth,
27139 html : this.fieldLabel
27143 cls : 'column col-md-' + (12 - this.labelWidth),
27153 cls : 'row clearfix',
27161 initEvents : function()
27163 this.managerEl = this.el.select('.roo-document-manager', true).first();
27164 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27166 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27167 this.selectorEl.hide();
27170 this.selectorEl.attr('multiple', 'multiple');
27173 this.selectorEl.on('change', this.onFileSelected, this);
27175 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27176 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27178 this.uploader.on('click', this.onUploaderClick, this);
27180 this.renderProgressDialog();
27184 window.addEventListener("resize", function() { _this.refresh(); } );
27186 this.fireEvent('initial', this);
27189 renderProgressDialog : function()
27193 this.progressDialog = new Roo.bootstrap.Modal({
27194 cls : 'roo-document-manager-progress-dialog',
27195 allow_close : false,
27205 btnclick : function() {
27206 _this.uploadCancel();
27212 this.progressDialog.render(Roo.get(document.body));
27214 this.progress = new Roo.bootstrap.Progress({
27215 cls : 'roo-document-manager-progress',
27220 this.progress.render(this.progressDialog.getChildContainer());
27222 this.progressBar = new Roo.bootstrap.ProgressBar({
27223 cls : 'roo-document-manager-progress-bar',
27226 aria_valuemax : 12,
27230 this.progressBar.render(this.progress.getChildContainer());
27233 onUploaderClick : function(e)
27235 e.preventDefault();
27237 if(this.fireEvent('beforeselectfile', this) != false){
27238 this.selectorEl.dom.click();
27243 onFileSelected : function(e)
27245 e.preventDefault();
27247 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27251 Roo.each(this.selectorEl.dom.files, function(file){
27252 if(this.fireEvent('inspect', this, file) != false){
27253 this.files.push(file);
27263 this.selectorEl.dom.value = '';
27265 if(!this.files.length){
27269 if(this.boxes > 0 && this.files.length > this.boxes){
27270 this.files = this.files.slice(0, this.boxes);
27273 this.uploader.show();
27275 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27276 this.uploader.hide();
27285 Roo.each(this.files, function(file){
27287 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27288 var f = this.renderPreview(file);
27293 if(file.type.indexOf('image') != -1){
27294 this.delegates.push(
27296 _this.process(file);
27297 }).createDelegate(this)
27305 _this.process(file);
27306 }).createDelegate(this)
27311 this.files = files;
27313 this.delegates = this.delegates.concat(docs);
27315 if(!this.delegates.length){
27320 this.progressBar.aria_valuemax = this.delegates.length;
27327 arrange : function()
27329 if(!this.delegates.length){
27330 this.progressDialog.hide();
27335 var delegate = this.delegates.shift();
27337 this.progressDialog.show();
27339 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27341 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27346 refresh : function()
27348 this.uploader.show();
27350 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27351 this.uploader.hide();
27354 Roo.isTouch ? this.closable(false) : this.closable(true);
27356 this.fireEvent('refresh', this);
27359 onRemove : function(e, el, o)
27361 e.preventDefault();
27363 this.fireEvent('remove', this, o);
27367 remove : function(o)
27371 Roo.each(this.files, function(file){
27372 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27381 this.files = files;
27388 Roo.each(this.files, function(file){
27393 file.target.remove();
27402 onClick : function(e, el, o)
27404 e.preventDefault();
27406 this.fireEvent('click', this, o);
27410 closable : function(closable)
27412 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27414 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27426 xhrOnLoad : function(xhr)
27428 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27432 if (xhr.readyState !== 4) {
27434 this.fireEvent('exception', this, xhr);
27438 var response = Roo.decode(xhr.responseText);
27440 if(!response.success){
27442 this.fireEvent('exception', this, xhr);
27446 var file = this.renderPreview(response.data);
27448 this.files.push(file);
27454 xhrOnError : function(xhr)
27456 Roo.log('xhr on error');
27458 var response = Roo.decode(xhr.responseText);
27465 process : function(file)
27467 if(this.fireEvent('process', this, file) !== false){
27468 if(this.editable && file.type.indexOf('image') != -1){
27469 this.fireEvent('edit', this, file);
27473 this.uploadStart(file, false);
27480 uploadStart : function(file, crop)
27482 this.xhr = new XMLHttpRequest();
27484 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27489 file.xhr = this.xhr;
27491 this.managerEl.createChild({
27493 cls : 'roo-document-manager-loading',
27497 tooltip : file.name,
27498 cls : 'roo-document-manager-thumb',
27499 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27505 this.xhr.open(this.method, this.url, true);
27508 "Accept": "application/json",
27509 "Cache-Control": "no-cache",
27510 "X-Requested-With": "XMLHttpRequest"
27513 for (var headerName in headers) {
27514 var headerValue = headers[headerName];
27516 this.xhr.setRequestHeader(headerName, headerValue);
27522 this.xhr.onload = function()
27524 _this.xhrOnLoad(_this.xhr);
27527 this.xhr.onerror = function()
27529 _this.xhrOnError(_this.xhr);
27532 var formData = new FormData();
27534 formData.append('returnHTML', 'NO');
27537 formData.append('crop', crop);
27540 formData.append(this.paramName, file, file.name);
27542 if(this.fireEvent('prepare', this, formData) != false){
27543 this.xhr.send(formData);
27547 uploadCancel : function()
27554 this.delegates = [];
27556 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27563 renderPreview : function(file)
27565 if(typeof(file.target) != 'undefined' && file.target){
27569 var previewEl = this.managerEl.createChild({
27571 cls : 'roo-document-manager-preview',
27575 tooltip : file.filename,
27576 cls : 'roo-document-manager-thumb',
27577 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27582 html : '<i class="fa fa-times-circle"></i>'
27587 var close = previewEl.select('button.close', true).first();
27589 close.on('click', this.onRemove, this, file);
27591 file.target = previewEl;
27593 var image = previewEl.select('img', true).first();
27597 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27599 image.on('click', this.onClick, this, file);
27605 onPreviewLoad : function(file, image)
27607 if(typeof(file.target) == 'undefined' || !file.target){
27611 var width = image.dom.naturalWidth || image.dom.width;
27612 var height = image.dom.naturalHeight || image.dom.height;
27614 if(width > height){
27615 file.target.addClass('wide');
27619 file.target.addClass('tall');
27624 uploadFromSource : function(file, crop)
27626 this.xhr = new XMLHttpRequest();
27628 this.managerEl.createChild({
27630 cls : 'roo-document-manager-loading',
27634 tooltip : file.name,
27635 cls : 'roo-document-manager-thumb',
27636 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27642 this.xhr.open(this.method, this.url, true);
27645 "Accept": "application/json",
27646 "Cache-Control": "no-cache",
27647 "X-Requested-With": "XMLHttpRequest"
27650 for (var headerName in headers) {
27651 var headerValue = headers[headerName];
27653 this.xhr.setRequestHeader(headerName, headerValue);
27659 this.xhr.onload = function()
27661 _this.xhrOnLoad(_this.xhr);
27664 this.xhr.onerror = function()
27666 _this.xhrOnError(_this.xhr);
27669 var formData = new FormData();
27671 formData.append('returnHTML', 'NO');
27673 formData.append('crop', crop);
27675 if(typeof(file.filename) != 'undefined'){
27676 formData.append('filename', file.filename);
27679 if(typeof(file.mimetype) != 'undefined'){
27680 formData.append('mimetype', file.mimetype);
27683 if(this.fireEvent('prepare', this, formData) != false){
27684 this.xhr.send(formData);
27694 * @class Roo.bootstrap.DocumentViewer
27695 * @extends Roo.bootstrap.Component
27696 * Bootstrap DocumentViewer class
27699 * Create a new DocumentViewer
27700 * @param {Object} config The config object
27703 Roo.bootstrap.DocumentViewer = function(config){
27704 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27709 * Fire after initEvent
27710 * @param {Roo.bootstrap.DocumentViewer} this
27716 * @param {Roo.bootstrap.DocumentViewer} this
27721 * Fire after trash button
27722 * @param {Roo.bootstrap.DocumentViewer} this
27729 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27731 getAutoCreate : function()
27735 cls : 'roo-document-viewer',
27739 cls : 'roo-document-viewer-body',
27743 cls : 'roo-document-viewer-thumb',
27747 cls : 'roo-document-viewer-image'
27755 cls : 'roo-document-viewer-footer',
27758 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27766 cls : 'btn btn-default roo-document-viewer-trash',
27767 html : '<i class="fa fa-trash"></i>'
27780 initEvents : function()
27783 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27784 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27786 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27787 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27789 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27790 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27792 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27793 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27795 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27796 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27798 this.bodyEl.on('click', this.onClick, this);
27800 this.trashBtn.on('click', this.onTrash, this);
27804 initial : function()
27806 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27809 this.fireEvent('initial', this);
27813 onClick : function(e)
27815 e.preventDefault();
27817 this.fireEvent('click', this);
27820 onTrash : function(e)
27822 e.preventDefault();
27824 this.fireEvent('trash', this);
27836 * @class Roo.bootstrap.NavProgressBar
27837 * @extends Roo.bootstrap.Component
27838 * Bootstrap NavProgressBar class
27841 * Create a new nav progress bar
27842 * @param {Object} config The config object
27845 Roo.bootstrap.NavProgressBar = function(config){
27846 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27848 this.bullets = this.bullets || [];
27850 // Roo.bootstrap.NavProgressBar.register(this);
27854 * Fires when the active item changes
27855 * @param {Roo.bootstrap.NavProgressBar} this
27856 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27857 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27864 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27869 getAutoCreate : function()
27871 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27875 cls : 'roo-navigation-bar-group',
27879 cls : 'roo-navigation-top-bar'
27883 cls : 'roo-navigation-bullets-bar',
27887 cls : 'roo-navigation-bar'
27894 cls : 'roo-navigation-bottom-bar'
27904 initEvents: function()
27909 onRender : function(ct, position)
27911 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27913 if(this.bullets.length){
27914 Roo.each(this.bullets, function(b){
27923 addItem : function(cfg)
27925 var item = new Roo.bootstrap.NavProgressItem(cfg);
27927 item.parentId = this.id;
27928 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27931 var top = new Roo.bootstrap.Element({
27933 cls : 'roo-navigation-bar-text'
27936 var bottom = new Roo.bootstrap.Element({
27938 cls : 'roo-navigation-bar-text'
27941 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27942 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27944 var topText = new Roo.bootstrap.Element({
27946 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27949 var bottomText = new Roo.bootstrap.Element({
27951 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27954 topText.onRender(top.el, null);
27955 bottomText.onRender(bottom.el, null);
27958 item.bottomEl = bottom;
27961 this.barItems.push(item);
27966 getActive : function()
27968 var active = false;
27970 Roo.each(this.barItems, function(v){
27972 if (!v.isActive()) {
27984 setActiveItem : function(item)
27988 Roo.each(this.barItems, function(v){
27989 if (v.rid == item.rid) {
27993 if (v.isActive()) {
27994 v.setActive(false);
27999 item.setActive(true);
28001 this.fireEvent('changed', this, item, prev);
28004 getBarItem: function(rid)
28008 Roo.each(this.barItems, function(e) {
28009 if (e.rid != rid) {
28020 indexOfItem : function(item)
28024 Roo.each(this.barItems, function(v, i){
28026 if (v.rid != item.rid) {
28037 setActiveNext : function()
28039 var i = this.indexOfItem(this.getActive());
28041 if (i > this.barItems.length) {
28045 this.setActiveItem(this.barItems[i+1]);
28048 setActivePrev : function()
28050 var i = this.indexOfItem(this.getActive());
28056 this.setActiveItem(this.barItems[i-1]);
28059 format : function()
28061 if(!this.barItems.length){
28065 var width = 100 / this.barItems.length;
28067 Roo.each(this.barItems, function(i){
28068 i.el.setStyle('width', width + '%');
28069 i.topEl.el.setStyle('width', width + '%');
28070 i.bottomEl.el.setStyle('width', width + '%');
28079 * Nav Progress Item
28084 * @class Roo.bootstrap.NavProgressItem
28085 * @extends Roo.bootstrap.Component
28086 * Bootstrap NavProgressItem class
28087 * @cfg {String} rid the reference id
28088 * @cfg {Boolean} active (true|false) Is item active default false
28089 * @cfg {Boolean} disabled (true|false) Is item active default false
28090 * @cfg {String} html
28091 * @cfg {String} position (top|bottom) text position default bottom
28092 * @cfg {String} icon show icon instead of number
28095 * Create a new NavProgressItem
28096 * @param {Object} config The config object
28098 Roo.bootstrap.NavProgressItem = function(config){
28099 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28104 * The raw click event for the entire grid.
28105 * @param {Roo.bootstrap.NavProgressItem} this
28106 * @param {Roo.EventObject} e
28113 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28119 position : 'bottom',
28122 getAutoCreate : function()
28124 var iconCls = 'roo-navigation-bar-item-icon';
28126 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28130 cls: 'roo-navigation-bar-item',
28140 cfg.cls += ' active';
28143 cfg.cls += ' disabled';
28149 disable : function()
28151 this.setDisabled(true);
28154 enable : function()
28156 this.setDisabled(false);
28159 initEvents: function()
28161 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28163 this.iconEl.on('click', this.onClick, this);
28166 onClick : function(e)
28168 e.preventDefault();
28174 if(this.fireEvent('click', this, e) === false){
28178 this.parent().setActiveItem(this);
28181 isActive: function ()
28183 return this.active;
28186 setActive : function(state)
28188 if(this.active == state){
28192 this.active = state;
28195 this.el.addClass('active');
28199 this.el.removeClass('active');
28204 setDisabled : function(state)
28206 if(this.disabled == state){
28210 this.disabled = state;
28213 this.el.addClass('disabled');
28217 this.el.removeClass('disabled');
28220 tooltipEl : function()
28222 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28235 * @class Roo.bootstrap.FieldLabel
28236 * @extends Roo.bootstrap.Component
28237 * Bootstrap FieldLabel class
28238 * @cfg {String} html contents of the element
28239 * @cfg {String} tag tag of the element default label
28240 * @cfg {String} cls class of the element
28241 * @cfg {String} target label target
28242 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28243 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28244 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28245 * @cfg {String} iconTooltip default "This field is required"
28248 * Create a new FieldLabel
28249 * @param {Object} config The config object
28252 Roo.bootstrap.FieldLabel = function(config){
28253 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28258 * Fires after the field has been marked as invalid.
28259 * @param {Roo.form.FieldLabel} this
28260 * @param {String} msg The validation message
28265 * Fires after the field has been validated with no errors.
28266 * @param {Roo.form.FieldLabel} this
28272 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28279 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28280 validClass : 'text-success fa fa-lg fa-check',
28281 iconTooltip : 'This field is required',
28283 getAutoCreate : function(){
28287 cls : 'roo-bootstrap-field-label ' + this.cls,
28293 tooltip : this.iconTooltip
28305 initEvents: function()
28307 Roo.bootstrap.Element.superclass.initEvents.call(this);
28309 this.iconEl = this.el.select('i', true).first();
28311 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28313 Roo.bootstrap.FieldLabel.register(this);
28317 * Mark this field as valid
28319 markValid : function()
28321 this.iconEl.show();
28323 this.iconEl.removeClass(this.invalidClass);
28325 this.iconEl.addClass(this.validClass);
28327 this.fireEvent('valid', this);
28331 * Mark this field as invalid
28332 * @param {String} msg The validation message
28334 markInvalid : function(msg)
28336 this.iconEl.show();
28338 this.iconEl.removeClass(this.validClass);
28340 this.iconEl.addClass(this.invalidClass);
28342 this.fireEvent('invalid', this, msg);
28348 Roo.apply(Roo.bootstrap.FieldLabel, {
28353 * register a FieldLabel Group
28354 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28356 register : function(label)
28358 if(this.groups.hasOwnProperty(label.target)){
28362 this.groups[label.target] = label;
28366 * fetch a FieldLabel Group based on the target
28367 * @param {string} target
28368 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28370 get: function(target) {
28371 if (typeof(this.groups[target]) == 'undefined') {
28375 return this.groups[target] ;
28384 * page DateSplitField.
28390 * @class Roo.bootstrap.DateSplitField
28391 * @extends Roo.bootstrap.Component
28392 * Bootstrap DateSplitField class
28393 * @cfg {string} fieldLabel - the label associated
28394 * @cfg {Number} labelWidth set the width of label (0-12)
28395 * @cfg {String} labelAlign (top|left)
28396 * @cfg {Boolean} dayAllowBlank (true|false) default false
28397 * @cfg {Boolean} monthAllowBlank (true|false) default false
28398 * @cfg {Boolean} yearAllowBlank (true|false) default false
28399 * @cfg {string} dayPlaceholder
28400 * @cfg {string} monthPlaceholder
28401 * @cfg {string} yearPlaceholder
28402 * @cfg {string} dayFormat default 'd'
28403 * @cfg {string} monthFormat default 'm'
28404 * @cfg {string} yearFormat default 'Y'
28408 * Create a new DateSplitField
28409 * @param {Object} config The config object
28412 Roo.bootstrap.DateSplitField = function(config){
28413 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28419 * getting the data of years
28420 * @param {Roo.bootstrap.DateSplitField} this
28421 * @param {Object} years
28426 * getting the data of days
28427 * @param {Roo.bootstrap.DateSplitField} this
28428 * @param {Object} days
28433 * Fires after the field has been marked as invalid.
28434 * @param {Roo.form.Field} this
28435 * @param {String} msg The validation message
28440 * Fires after the field has been validated with no errors.
28441 * @param {Roo.form.Field} this
28447 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28450 labelAlign : 'top',
28452 dayAllowBlank : false,
28453 monthAllowBlank : false,
28454 yearAllowBlank : false,
28455 dayPlaceholder : '',
28456 monthPlaceholder : '',
28457 yearPlaceholder : '',
28461 isFormField : true,
28463 getAutoCreate : function()
28467 cls : 'row roo-date-split-field-group',
28472 cls : 'form-hidden-field roo-date-split-field-group-value',
28478 if(this.fieldLabel){
28481 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28485 html : this.fieldLabel
28491 Roo.each(['day', 'month', 'year'], function(t){
28494 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28501 inputEl: function ()
28503 return this.el.select('.roo-date-split-field-group-value', true).first();
28506 onRender : function(ct, position)
28510 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28512 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28514 this.dayField = new Roo.bootstrap.ComboBox({
28515 allowBlank : this.dayAllowBlank,
28516 alwaysQuery : true,
28517 displayField : 'value',
28520 forceSelection : true,
28522 placeholder : this.dayPlaceholder,
28523 selectOnFocus : true,
28524 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28525 triggerAction : 'all',
28527 valueField : 'value',
28528 store : new Roo.data.SimpleStore({
28529 data : (function() {
28531 _this.fireEvent('days', _this, days);
28534 fields : [ 'value' ]
28537 select : function (_self, record, index)
28539 _this.setValue(_this.getValue());
28544 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28546 this.monthField = new Roo.bootstrap.MonthField({
28547 after : '<i class=\"fa fa-calendar\"></i>',
28548 allowBlank : this.monthAllowBlank,
28549 placeholder : this.monthPlaceholder,
28552 render : function (_self)
28554 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28555 e.preventDefault();
28559 select : function (_self, oldvalue, newvalue)
28561 _this.setValue(_this.getValue());
28566 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28568 this.yearField = new Roo.bootstrap.ComboBox({
28569 allowBlank : this.yearAllowBlank,
28570 alwaysQuery : true,
28571 displayField : 'value',
28574 forceSelection : true,
28576 placeholder : this.yearPlaceholder,
28577 selectOnFocus : true,
28578 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28579 triggerAction : 'all',
28581 valueField : 'value',
28582 store : new Roo.data.SimpleStore({
28583 data : (function() {
28585 _this.fireEvent('years', _this, years);
28588 fields : [ 'value' ]
28591 select : function (_self, record, index)
28593 _this.setValue(_this.getValue());
28598 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28601 setValue : function(v, format)
28603 this.inputEl.dom.value = v;
28605 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28607 var d = Date.parseDate(v, f);
28614 this.setDay(d.format(this.dayFormat));
28615 this.setMonth(d.format(this.monthFormat));
28616 this.setYear(d.format(this.yearFormat));
28623 setDay : function(v)
28625 this.dayField.setValue(v);
28626 this.inputEl.dom.value = this.getValue();
28631 setMonth : function(v)
28633 this.monthField.setValue(v, true);
28634 this.inputEl.dom.value = this.getValue();
28639 setYear : function(v)
28641 this.yearField.setValue(v);
28642 this.inputEl.dom.value = this.getValue();
28647 getDay : function()
28649 return this.dayField.getValue();
28652 getMonth : function()
28654 return this.monthField.getValue();
28657 getYear : function()
28659 return this.yearField.getValue();
28662 getValue : function()
28664 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28666 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28676 this.inputEl.dom.value = '';
28681 validate : function()
28683 var d = this.dayField.validate();
28684 var m = this.monthField.validate();
28685 var y = this.yearField.validate();
28690 (!this.dayAllowBlank && !d) ||
28691 (!this.monthAllowBlank && !m) ||
28692 (!this.yearAllowBlank && !y)
28697 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28706 this.markInvalid();
28711 markValid : function()
28714 var label = this.el.select('label', true).first();
28715 var icon = this.el.select('i.fa-star', true).first();
28721 this.fireEvent('valid', this);
28725 * Mark this field as invalid
28726 * @param {String} msg The validation message
28728 markInvalid : function(msg)
28731 var label = this.el.select('label', true).first();
28732 var icon = this.el.select('i.fa-star', true).first();
28734 if(label && !icon){
28735 this.el.select('.roo-date-split-field-label', true).createChild({
28737 cls : 'text-danger fa fa-lg fa-star',
28738 tooltip : 'This field is required',
28739 style : 'margin-right:5px;'
28743 this.fireEvent('invalid', this, msg);
28746 clearInvalid : function()
28748 var label = this.el.select('label', true).first();
28749 var icon = this.el.select('i.fa-star', true).first();
28755 this.fireEvent('valid', this);
28758 getName: function()
28768 * http://masonry.desandro.com
28770 * The idea is to render all the bricks based on vertical width...
28772 * The original code extends 'outlayer' - we might need to use that....
28778 * @class Roo.bootstrap.LayoutMasonry
28779 * @extends Roo.bootstrap.Component
28780 * Bootstrap Layout Masonry class
28783 * Create a new Element
28784 * @param {Object} config The config object
28787 Roo.bootstrap.LayoutMasonry = function(config){
28788 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28794 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
28797 * @cfg {Boolean} isLayoutInstant = no animation?
28799 isLayoutInstant : false, // needed?
28802 * @cfg {Number} boxWidth width of the columns
28807 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
28812 * @cfg {Number} padWidth padding below box..
28817 * @cfg {Number} gutter gutter width..
28822 * @cfg {Number} maxCols maximum number of columns
28828 * @cfg {Boolean} isAutoInitial defalut true
28830 isAutoInitial : true,
28835 * @cfg {Boolean} isHorizontal defalut false
28837 isHorizontal : false,
28839 currentSize : null,
28845 bricks: null, //CompositeElement
28849 _isLayoutInited : false,
28851 // isAlternative : false, // only use for vertical layout...
28854 * @cfg {Number} alternativePadWidth padding below box..
28856 alternativePadWidth : 50,
28858 getAutoCreate : function(){
28862 cls: 'blog-masonary-wrapper ' + this.cls,
28864 cls : 'mas-boxes masonary'
28871 getChildContainer: function( )
28873 if (this.boxesEl) {
28874 return this.boxesEl;
28877 this.boxesEl = this.el.select('.mas-boxes').first();
28879 return this.boxesEl;
28883 initEvents : function()
28887 if(this.isAutoInitial){
28888 Roo.log('hook children rendered');
28889 this.on('childrenrendered', function() {
28890 Roo.log('children rendered');
28896 initial : function()
28898 this.currentSize = this.el.getBox(true);
28900 Roo.EventManager.onWindowResize(this.resize, this);
28902 if(!this.isAutoInitial){
28910 //this.layout.defer(500,this);
28914 resize : function()
28918 var cs = this.el.getBox(true);
28920 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28921 Roo.log("no change in with or X");
28925 this.currentSize = cs;
28931 layout : function()
28933 this._resetLayout();
28935 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28937 this.layoutItems( isInstant );
28939 this._isLayoutInited = true;
28943 _resetLayout : function()
28945 if(this.isHorizontal){
28946 this.horizontalMeasureColumns();
28950 this.verticalMeasureColumns();
28954 verticalMeasureColumns : function()
28956 this.getContainerWidth();
28958 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28959 // this.colWidth = Math.floor(this.containerWidth * 0.8);
28963 var boxWidth = this.boxWidth + this.padWidth;
28965 if(this.containerWidth < this.boxWidth){
28966 boxWidth = this.containerWidth
28969 var containerWidth = this.containerWidth;
28971 var cols = Math.floor(containerWidth / boxWidth);
28973 this.cols = Math.max( cols, 1 );
28975 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28977 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28979 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28981 this.colWidth = boxWidth + avail - this.padWidth;
28983 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28984 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
28987 horizontalMeasureColumns : function()
28989 this.getContainerWidth();
28991 var boxWidth = this.boxWidth;
28993 if(this.containerWidth < boxWidth){
28994 boxWidth = this.containerWidth;
28997 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28999 this.el.setHeight(boxWidth);
29003 getContainerWidth : function()
29005 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29008 layoutItems : function( isInstant )
29010 var items = Roo.apply([], this.bricks);
29012 if(this.isHorizontal){
29013 this._horizontalLayoutItems( items , isInstant );
29017 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29018 // this._verticalAlternativeLayoutItems( items , isInstant );
29022 this._verticalLayoutItems( items , isInstant );
29026 _verticalLayoutItems : function ( items , isInstant)
29028 if ( !items || !items.length ) {
29033 ['xs', 'xs', 'xs', 'tall'],
29034 ['xs', 'xs', 'tall'],
29035 ['xs', 'xs', 'sm'],
29036 ['xs', 'xs', 'xs'],
29042 ['sm', 'xs', 'xs'],
29046 ['tall', 'xs', 'xs', 'xs'],
29047 ['tall', 'xs', 'xs'],
29059 Roo.each(items, function(item, k){
29061 switch (item.size) {
29062 // these layouts take up a full box,
29073 boxes.push([item]);
29096 var filterPattern = function(box, length)
29104 var pattern = box.slice(0, length);
29108 Roo.each(pattern, function(i){
29109 format.push(i.size);
29112 Roo.each(standard, function(s){
29114 if(String(s) != String(format)){
29123 if(!match && length == 1){
29128 filterPattern(box, length - 1);
29132 queue.push(pattern);
29134 box = box.slice(length, box.length);
29136 filterPattern(box, 4);
29142 Roo.each(boxes, function(box, k){
29148 if(box.length == 1){
29153 filterPattern(box, 4);
29157 this._processVerticalLayoutQueue( queue, isInstant );
29161 // _verticalAlternativeLayoutItems : function( items , isInstant )
29163 // if ( !items || !items.length ) {
29167 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29171 _horizontalLayoutItems : function ( items , isInstant)
29173 if ( !items || !items.length || items.length < 3) {
29179 var eItems = items.slice(0, 3);
29181 items = items.slice(3, items.length);
29184 ['xs', 'xs', 'xs', 'wide'],
29185 ['xs', 'xs', 'wide'],
29186 ['xs', 'xs', 'sm'],
29187 ['xs', 'xs', 'xs'],
29193 ['sm', 'xs', 'xs'],
29197 ['wide', 'xs', 'xs', 'xs'],
29198 ['wide', 'xs', 'xs'],
29211 Roo.each(items, function(item, k){
29213 switch (item.size) {
29224 boxes.push([item]);
29248 var filterPattern = function(box, length)
29256 var pattern = box.slice(0, length);
29260 Roo.each(pattern, function(i){
29261 format.push(i.size);
29264 Roo.each(standard, function(s){
29266 if(String(s) != String(format)){
29275 if(!match && length == 1){
29280 filterPattern(box, length - 1);
29284 queue.push(pattern);
29286 box = box.slice(length, box.length);
29288 filterPattern(box, 4);
29294 Roo.each(boxes, function(box, k){
29300 if(box.length == 1){
29305 filterPattern(box, 4);
29312 var pos = this.el.getBox(true);
29316 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29318 var hit_end = false;
29320 Roo.each(queue, function(box){
29324 Roo.each(box, function(b){
29326 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29336 Roo.each(box, function(b){
29338 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29341 mx = Math.max(mx, b.x);
29345 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29349 Roo.each(box, function(b){
29351 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29365 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29368 /** Sets position of item in DOM
29369 * @param {Element} item
29370 * @param {Number} x - horizontal position
29371 * @param {Number} y - vertical position
29372 * @param {Boolean} isInstant - disables transitions
29374 _processVerticalLayoutQueue : function( queue, isInstant )
29376 var pos = this.el.getBox(true);
29381 for (var i = 0; i < this.cols; i++){
29385 Roo.each(queue, function(box, k){
29387 var col = k % this.cols;
29389 Roo.each(box, function(b,kk){
29391 b.el.position('absolute');
29393 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29394 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29396 if(b.size == 'md-left' || b.size == 'md-right'){
29397 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29398 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29401 b.el.setWidth(width);
29402 b.el.setHeight(height);
29404 b.el.select('iframe',true).setSize(width,height);
29408 for (var i = 0; i < this.cols; i++){
29410 if(maxY[i] < maxY[col]){
29415 col = Math.min(col, i);
29419 x = pos.x + col * (this.colWidth + this.padWidth);
29423 var positions = [];
29425 switch (box.length){
29427 positions = this.getVerticalOneBoxColPositions(x, y, box);
29430 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29433 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29436 positions = this.getVerticalFourBoxColPositions(x, y, box);
29442 Roo.each(box, function(b,kk){
29444 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29446 var sz = b.el.getSize();
29448 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29456 for (var i = 0; i < this.cols; i++){
29457 mY = Math.max(mY, maxY[i]);
29460 this.el.setHeight(mY - pos.y);
29464 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29466 // var pos = this.el.getBox(true);
29469 // var maxX = pos.right;
29471 // var maxHeight = 0;
29473 // Roo.each(items, function(item, k){
29477 // item.el.position('absolute');
29479 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29481 // item.el.setWidth(width);
29483 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29485 // item.el.setHeight(height);
29488 // item.el.setXY([x, y], isInstant ? false : true);
29490 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29493 // y = y + height + this.alternativePadWidth;
29495 // maxHeight = maxHeight + height + this.alternativePadWidth;
29499 // this.el.setHeight(maxHeight);
29503 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29505 var pos = this.el.getBox(true);
29510 var maxX = pos.right;
29512 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29514 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29516 Roo.each(queue, function(box, k){
29518 Roo.each(box, function(b, kk){
29520 b.el.position('absolute');
29522 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29523 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29525 if(b.size == 'md-left' || b.size == 'md-right'){
29526 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29527 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29530 b.el.setWidth(width);
29531 b.el.setHeight(height);
29539 var positions = [];
29541 switch (box.length){
29543 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29546 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29549 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29552 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29558 Roo.each(box, function(b,kk){
29560 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29562 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29570 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29572 Roo.each(eItems, function(b,k){
29574 b.size = (k == 0) ? 'sm' : 'xs';
29575 b.x = (k == 0) ? 2 : 1;
29576 b.y = (k == 0) ? 2 : 1;
29578 b.el.position('absolute');
29580 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29582 b.el.setWidth(width);
29584 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29586 b.el.setHeight(height);
29590 var positions = [];
29593 x : maxX - this.unitWidth * 2 - this.gutter,
29598 x : maxX - this.unitWidth,
29599 y : minY + (this.unitWidth + this.gutter) * 2
29603 x : maxX - this.unitWidth * 3 - this.gutter * 2,
29607 Roo.each(eItems, function(b,k){
29609 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29615 getVerticalOneBoxColPositions : function(x, y, box)
29619 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29621 if(box[0].size == 'md-left'){
29625 if(box[0].size == 'md-right'){
29630 x : x + (this.unitWidth + this.gutter) * rand,
29637 getVerticalTwoBoxColPositions : function(x, y, box)
29641 if(box[0].size == 'xs'){
29645 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29649 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29663 x : x + (this.unitWidth + this.gutter) * 2,
29664 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29671 getVerticalThreeBoxColPositions : function(x, y, box)
29675 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29683 x : x + (this.unitWidth + this.gutter) * 1,
29688 x : x + (this.unitWidth + this.gutter) * 2,
29696 if(box[0].size == 'xs' && box[1].size == 'xs'){
29705 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29709 x : x + (this.unitWidth + this.gutter) * 1,
29723 x : x + (this.unitWidth + this.gutter) * 2,
29728 x : x + (this.unitWidth + this.gutter) * 2,
29729 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29736 getVerticalFourBoxColPositions : function(x, y, box)
29740 if(box[0].size == 'xs'){
29749 y : y + (this.unitHeight + this.gutter) * 1
29754 y : y + (this.unitHeight + this.gutter) * 2
29758 x : x + (this.unitWidth + this.gutter) * 1,
29772 x : x + (this.unitWidth + this.gutter) * 2,
29777 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29778 y : y + (this.unitHeight + this.gutter) * 1
29782 x : x + (this.unitWidth + this.gutter) * 2,
29783 y : y + (this.unitWidth + this.gutter) * 2
29790 getHorizontalOneBoxColPositions : function(maxX, minY, box)
29794 if(box[0].size == 'md-left'){
29796 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29803 if(box[0].size == 'md-right'){
29805 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29806 y : minY + (this.unitWidth + this.gutter) * 1
29812 var rand = Math.floor(Math.random() * (4 - box[0].y));
29815 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29816 y : minY + (this.unitWidth + this.gutter) * rand
29823 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29827 if(box[0].size == 'xs'){
29830 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29835 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29836 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29844 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29849 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29850 y : minY + (this.unitWidth + this.gutter) * 2
29857 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29861 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29864 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29869 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29870 y : minY + (this.unitWidth + this.gutter) * 1
29874 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29875 y : minY + (this.unitWidth + this.gutter) * 2
29882 if(box[0].size == 'xs' && box[1].size == 'xs'){
29885 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29890 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29895 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29896 y : minY + (this.unitWidth + this.gutter) * 1
29904 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29909 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29910 y : minY + (this.unitWidth + this.gutter) * 2
29914 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29915 y : minY + (this.unitWidth + this.gutter) * 2
29922 getHorizontalFourBoxColPositions : function(maxX, minY, box)
29926 if(box[0].size == 'xs'){
29929 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29934 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29939 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),
29944 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29945 y : minY + (this.unitWidth + this.gutter) * 1
29953 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29958 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29959 y : minY + (this.unitWidth + this.gutter) * 2
29963 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29964 y : minY + (this.unitWidth + this.gutter) * 2
29968 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),
29969 y : minY + (this.unitWidth + this.gutter) * 2
29983 * http://masonry.desandro.com
29985 * The idea is to render all the bricks based on vertical width...
29987 * The original code extends 'outlayer' - we might need to use that....
29993 * @class Roo.bootstrap.LayoutMasonryAuto
29994 * @extends Roo.bootstrap.Component
29995 * Bootstrap Layout Masonry class
29998 * Create a new Element
29999 * @param {Object} config The config object
30002 Roo.bootstrap.LayoutMasonryAuto = function(config){
30003 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30006 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30009 * @cfg {Boolean} isFitWidth - resize the width..
30011 isFitWidth : false, // options..
30013 * @cfg {Boolean} isOriginLeft = left align?
30015 isOriginLeft : true,
30017 * @cfg {Boolean} isOriginTop = top align?
30019 isOriginTop : false,
30021 * @cfg {Boolean} isLayoutInstant = no animation?
30023 isLayoutInstant : false, // needed?
30025 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30027 isResizingContainer : true,
30029 * @cfg {Number} columnWidth width of the columns
30035 * @cfg {Number} maxCols maximum number of columns
30040 * @cfg {Number} padHeight padding below box..
30046 * @cfg {Boolean} isAutoInitial defalut true
30049 isAutoInitial : true,
30055 initialColumnWidth : 0,
30056 currentSize : null,
30058 colYs : null, // array.
30065 bricks: null, //CompositeElement
30066 cols : 0, // array?
30067 // element : null, // wrapped now this.el
30068 _isLayoutInited : null,
30071 getAutoCreate : function(){
30075 cls: 'blog-masonary-wrapper ' + this.cls,
30077 cls : 'mas-boxes masonary'
30084 getChildContainer: function( )
30086 if (this.boxesEl) {
30087 return this.boxesEl;
30090 this.boxesEl = this.el.select('.mas-boxes').first();
30092 return this.boxesEl;
30096 initEvents : function()
30100 if(this.isAutoInitial){
30101 Roo.log('hook children rendered');
30102 this.on('childrenrendered', function() {
30103 Roo.log('children rendered');
30110 initial : function()
30112 this.reloadItems();
30114 this.currentSize = this.el.getBox(true);
30116 /// was window resize... - let's see if this works..
30117 Roo.EventManager.onWindowResize(this.resize, this);
30119 if(!this.isAutoInitial){
30124 this.layout.defer(500,this);
30127 reloadItems: function()
30129 this.bricks = this.el.select('.masonry-brick', true);
30131 this.bricks.each(function(b) {
30132 //Roo.log(b.getSize());
30133 if (!b.attr('originalwidth')) {
30134 b.attr('originalwidth', b.getSize().width);
30139 Roo.log(this.bricks.elements.length);
30142 resize : function()
30145 var cs = this.el.getBox(true);
30147 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30148 Roo.log("no change in with or X");
30151 this.currentSize = cs;
30155 layout : function()
30158 this._resetLayout();
30159 //this._manageStamps();
30161 // don't animate first layout
30162 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30163 this.layoutItems( isInstant );
30165 // flag for initalized
30166 this._isLayoutInited = true;
30169 layoutItems : function( isInstant )
30171 //var items = this._getItemsForLayout( this.items );
30172 // original code supports filtering layout items.. we just ignore it..
30174 this._layoutItems( this.bricks , isInstant );
30176 this._postLayout();
30178 _layoutItems : function ( items , isInstant)
30180 //this.fireEvent( 'layout', this, items );
30183 if ( !items || !items.elements.length ) {
30184 // no items, emit event with empty array
30189 items.each(function(item) {
30190 Roo.log("layout item");
30192 // get x/y object from method
30193 var position = this._getItemLayoutPosition( item );
30195 position.item = item;
30196 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30197 queue.push( position );
30200 this._processLayoutQueue( queue );
30202 /** Sets position of item in DOM
30203 * @param {Element} item
30204 * @param {Number} x - horizontal position
30205 * @param {Number} y - vertical position
30206 * @param {Boolean} isInstant - disables transitions
30208 _processLayoutQueue : function( queue )
30210 for ( var i=0, len = queue.length; i < len; i++ ) {
30211 var obj = queue[i];
30212 obj.item.position('absolute');
30213 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30219 * Any logic you want to do after each layout,
30220 * i.e. size the container
30222 _postLayout : function()
30224 this.resizeContainer();
30227 resizeContainer : function()
30229 if ( !this.isResizingContainer ) {
30232 var size = this._getContainerSize();
30234 this.el.setSize(size.width,size.height);
30235 this.boxesEl.setSize(size.width,size.height);
30241 _resetLayout : function()
30243 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30244 this.colWidth = this.el.getWidth();
30245 //this.gutter = this.el.getWidth();
30247 this.measureColumns();
30253 this.colYs.push( 0 );
30259 measureColumns : function()
30261 this.getContainerWidth();
30262 // if columnWidth is 0, default to outerWidth of first item
30263 if ( !this.columnWidth ) {
30264 var firstItem = this.bricks.first();
30265 Roo.log(firstItem);
30266 this.columnWidth = this.containerWidth;
30267 if (firstItem && firstItem.attr('originalwidth') ) {
30268 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30270 // columnWidth fall back to item of first element
30271 Roo.log("set column width?");
30272 this.initialColumnWidth = this.columnWidth ;
30274 // if first elem has no width, default to size of container
30279 if (this.initialColumnWidth) {
30280 this.columnWidth = this.initialColumnWidth;
30285 // column width is fixed at the top - however if container width get's smaller we should
30288 // this bit calcs how man columns..
30290 var columnWidth = this.columnWidth += this.gutter;
30292 // calculate columns
30293 var containerWidth = this.containerWidth + this.gutter;
30295 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30296 // fix rounding errors, typically with gutters
30297 var excess = columnWidth - containerWidth % columnWidth;
30300 // if overshoot is less than a pixel, round up, otherwise floor it
30301 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30302 cols = Math[ mathMethod ]( cols );
30303 this.cols = Math.max( cols, 1 );
30304 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30306 // padding positioning..
30307 var totalColWidth = this.cols * this.columnWidth;
30308 var padavail = this.containerWidth - totalColWidth;
30309 // so for 2 columns - we need 3 'pads'
30311 var padNeeded = (1+this.cols) * this.padWidth;
30313 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30315 this.columnWidth += padExtra
30316 //this.padWidth = Math.floor(padavail / ( this.cols));
30318 // adjust colum width so that padding is fixed??
30320 // we have 3 columns ... total = width * 3
30321 // we have X left over... that should be used by
30323 //if (this.expandC) {
30331 getContainerWidth : function()
30333 /* // container is parent if fit width
30334 var container = this.isFitWidth ? this.element.parentNode : this.element;
30335 // check that this.size and size are there
30336 // IE8 triggers resize on body size change, so they might not be
30338 var size = getSize( container ); //FIXME
30339 this.containerWidth = size && size.innerWidth; //FIXME
30342 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30346 _getItemLayoutPosition : function( item ) // what is item?
30348 // we resize the item to our columnWidth..
30350 item.setWidth(this.columnWidth);
30351 item.autoBoxAdjust = false;
30353 var sz = item.getSize();
30355 // how many columns does this brick span
30356 var remainder = this.containerWidth % this.columnWidth;
30358 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30359 // round if off by 1 pixel, otherwise use ceil
30360 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30361 colSpan = Math.min( colSpan, this.cols );
30363 // normally this should be '1' as we dont' currently allow multi width columns..
30365 var colGroup = this._getColGroup( colSpan );
30366 // get the minimum Y value from the columns
30367 var minimumY = Math.min.apply( Math, colGroup );
30368 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30370 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30372 // position the brick
30374 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30375 y: this.currentSize.y + minimumY + this.padHeight
30379 // apply setHeight to necessary columns
30380 var setHeight = minimumY + sz.height + this.padHeight;
30381 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30383 var setSpan = this.cols + 1 - colGroup.length;
30384 for ( var i = 0; i < setSpan; i++ ) {
30385 this.colYs[ shortColIndex + i ] = setHeight ;
30392 * @param {Number} colSpan - number of columns the element spans
30393 * @returns {Array} colGroup
30395 _getColGroup : function( colSpan )
30397 if ( colSpan < 2 ) {
30398 // if brick spans only one column, use all the column Ys
30403 // how many different places could this brick fit horizontally
30404 var groupCount = this.cols + 1 - colSpan;
30405 // for each group potential horizontal position
30406 for ( var i = 0; i < groupCount; i++ ) {
30407 // make an array of colY values for that one group
30408 var groupColYs = this.colYs.slice( i, i + colSpan );
30409 // and get the max value of the array
30410 colGroup[i] = Math.max.apply( Math, groupColYs );
30415 _manageStamp : function( stamp )
30417 var stampSize = stamp.getSize();
30418 var offset = stamp.getBox();
30419 // get the columns that this stamp affects
30420 var firstX = this.isOriginLeft ? offset.x : offset.right;
30421 var lastX = firstX + stampSize.width;
30422 var firstCol = Math.floor( firstX / this.columnWidth );
30423 firstCol = Math.max( 0, firstCol );
30425 var lastCol = Math.floor( lastX / this.columnWidth );
30426 // lastCol should not go over if multiple of columnWidth #425
30427 lastCol -= lastX % this.columnWidth ? 0 : 1;
30428 lastCol = Math.min( this.cols - 1, lastCol );
30430 // set colYs to bottom of the stamp
30431 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30434 for ( var i = firstCol; i <= lastCol; i++ ) {
30435 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30440 _getContainerSize : function()
30442 this.maxY = Math.max.apply( Math, this.colYs );
30447 if ( this.isFitWidth ) {
30448 size.width = this._getContainerFitWidth();
30454 _getContainerFitWidth : function()
30456 var unusedCols = 0;
30457 // count unused columns
30460 if ( this.colYs[i] !== 0 ) {
30465 // fit container to columns that have been used
30466 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30469 needsResizeLayout : function()
30471 var previousWidth = this.containerWidth;
30472 this.getContainerWidth();
30473 return previousWidth !== this.containerWidth;
30488 * @class Roo.bootstrap.MasonryBrick
30489 * @extends Roo.bootstrap.Component
30490 * Bootstrap MasonryBrick class
30493 * Create a new MasonryBrick
30494 * @param {Object} config The config object
30497 Roo.bootstrap.MasonryBrick = function(config){
30498 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30504 * When a MasonryBrick is clcik
30505 * @param {Roo.bootstrap.MasonryBrick} this
30506 * @param {Roo.EventObject} e
30512 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30515 * @cfg {String} title
30519 * @cfg {String} html
30523 * @cfg {String} bgimage
30527 * @cfg {String} videourl
30531 * @cfg {String} cls
30535 * @cfg {String} href
30539 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30544 * @cfg {String} (center|bottom) placetitle
30548 getAutoCreate : function()
30550 var cls = 'masonry-brick';
30552 if(this.href.length){
30553 cls += ' masonry-brick-link';
30556 if(this.bgimage.length){
30557 cls += ' masonry-brick-image';
30561 cls += ' masonry-' + this.size + '-brick';
30564 if(this.placetitle.length){
30566 switch (this.placetitle) {
30568 cls += ' masonry-center-title';
30571 cls += ' masonry-bottom-title';
30578 if(!this.html.length && !this.bgimage.length){
30579 cls += ' masonry-center-title';
30582 if(!this.html.length && this.bgimage.length){
30583 cls += ' masonry-bottom-title';
30588 cls += ' ' + this.cls;
30592 tag: (this.href.length) ? 'a' : 'div',
30597 cls: 'masonry-brick-paragraph',
30603 if(this.href.length){
30604 cfg.href = this.href;
30607 var cn = cfg.cn[0].cn;
30609 if(this.title.length){
30612 cls: 'masonry-brick-title',
30617 if(this.html.length){
30620 cls: 'masonry-brick-text',
30624 if (!this.title.length && !this.html.length) {
30625 cfg.cn[0].cls += ' hide';
30628 if(this.bgimage.length){
30631 cls: 'masonry-brick-image-view',
30635 if(this.videourl.length){
30636 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30637 // youtube support only?
30640 cls: 'masonry-brick-image-view',
30643 allowfullscreen : true
30652 initEvents: function()
30654 switch (this.size) {
30656 // this.intSize = 1;
30661 // this.intSize = 2;
30668 // this.intSize = 3;
30673 // this.intSize = 3;
30678 // this.intSize = 3;
30683 // this.intSize = 3;
30695 this.el.on('touchstart', this.onTouchStart, this);
30696 this.el.on('touchmove', this.onTouchMove, this);
30697 this.el.on('touchend', this.onTouchEnd, this);
30698 this.el.on('contextmenu', this.onContextMenu, this);
30700 this.el.on('mouseenter' ,this.enter, this);
30701 this.el.on('mouseleave', this.leave, this);
30704 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30705 this.parent().bricks.push(this);
30710 onClick: function(e, el)
30716 var time = this.endTimer - this.startTimer;
30724 e.preventDefault();
30727 enter: function(e, el)
30729 e.preventDefault();
30731 if(this.bgimage.length && this.html.length){
30732 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30736 leave: function(e, el)
30738 e.preventDefault();
30740 if(this.bgimage.length && this.html.length){
30741 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30745 onTouchStart: function(e, el)
30747 // e.preventDefault();
30749 this.touchmoved = false;
30751 if(!this.bgimage.length || !this.html.length){
30755 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30757 this.timer = new Date().getTime();
30761 onTouchMove: function(e, el)
30763 this.touchmoved = true;
30766 onContextMenu : function(e,el)
30768 e.preventDefault();
30769 e.stopPropagation();
30773 onTouchEnd: function(e, el)
30775 // e.preventDefault();
30777 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30784 if(!this.bgimage.length || !this.html.length){
30786 if(this.href.length){
30787 window.location.href = this.href;
30793 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30795 window.location.href = this.href;
30810 * @class Roo.bootstrap.Brick
30811 * @extends Roo.bootstrap.Component
30812 * Bootstrap Brick class
30815 * Create a new Brick
30816 * @param {Object} config The config object
30819 Roo.bootstrap.Brick = function(config){
30820 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30826 * When a Brick is click
30827 * @param {Roo.bootstrap.Brick} this
30828 * @param {Roo.EventObject} e
30834 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
30837 * @cfg {String} title
30841 * @cfg {String} html
30845 * @cfg {String} bgimage
30849 * @cfg {String} cls
30853 * @cfg {String} href
30857 * @cfg {String} video
30861 * @cfg {Boolean} square
30865 getAutoCreate : function()
30867 var cls = 'roo-brick';
30869 if(this.href.length){
30870 cls += ' roo-brick-link';
30873 if(this.bgimage.length){
30874 cls += ' roo-brick-image';
30877 if(!this.html.length && !this.bgimage.length){
30878 cls += ' roo-brick-center-title';
30881 if(!this.html.length && this.bgimage.length){
30882 cls += ' roo-brick-bottom-title';
30886 cls += ' ' + this.cls;
30890 tag: (this.href.length) ? 'a' : 'div',
30895 cls: 'roo-brick-paragraph',
30901 if(this.href.length){
30902 cfg.href = this.href;
30905 var cn = cfg.cn[0].cn;
30907 if(this.title.length){
30910 cls: 'roo-brick-title',
30915 if(this.html.length){
30918 cls: 'roo-brick-text',
30925 if(this.bgimage.length){
30928 cls: 'roo-brick-image-view',
30936 initEvents: function()
30938 if(this.title.length || this.html.length){
30939 this.el.on('mouseenter' ,this.enter, this);
30940 this.el.on('mouseleave', this.leave, this);
30944 Roo.EventManager.onWindowResize(this.resize, this);
30949 resize : function()
30951 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30953 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30954 // paragraph.setHeight(paragraph.getWidth());
30956 if(this.bgimage.length){
30957 var image = this.el.select('.roo-brick-image-view', true).first();
30958 image.setWidth(paragraph.getWidth());
30959 image.setHeight(paragraph.getWidth());
30964 enter: function(e, el)
30966 e.preventDefault();
30968 if(this.bgimage.length){
30969 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30970 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30974 leave: function(e, el)
30976 e.preventDefault();
30978 if(this.bgimage.length){
30979 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30980 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30990 * Ext JS Library 1.1.1
30991 * Copyright(c) 2006-2007, Ext JS, LLC.
30993 * Originally Released Under LGPL - original licence link has changed is not relivant.
30996 * <script type="text/javascript">
31001 * @class Roo.bootstrap.SplitBar
31002 * @extends Roo.util.Observable
31003 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31007 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31008 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31009 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31010 split.minSize = 100;
31011 split.maxSize = 600;
31012 split.animate = true;
31013 split.on('moved', splitterMoved);
31016 * Create a new SplitBar
31017 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
31018 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
31019 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31020 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
31021 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31022 position of the SplitBar).
31024 Roo.bootstrap.SplitBar = function(cfg){
31029 // dragElement : elm
31030 // resizingElement: el,
31032 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31033 // placement : Roo.bootstrap.SplitBar.LEFT ,
31034 // existingProxy ???
31037 this.el = Roo.get(cfg.dragElement, true);
31038 this.el.dom.unselectable = "on";
31040 this.resizingEl = Roo.get(cfg.resizingElement, true);
31044 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31045 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31048 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31051 * The minimum size of the resizing element. (Defaults to 0)
31057 * The maximum size of the resizing element. (Defaults to 2000)
31060 this.maxSize = 2000;
31063 * Whether to animate the transition to the new size
31066 this.animate = false;
31069 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31072 this.useShim = false;
31077 if(!cfg.existingProxy){
31079 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31081 this.proxy = Roo.get(cfg.existingProxy).dom;
31084 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31087 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31090 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31093 this.dragSpecs = {};
31096 * @private The adapter to use to positon and resize elements
31098 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31099 this.adapter.init(this);
31101 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31103 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31104 this.el.addClass("roo-splitbar-h");
31107 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31108 this.el.addClass("roo-splitbar-v");
31114 * Fires when the splitter is moved (alias for {@link #event-moved})
31115 * @param {Roo.bootstrap.SplitBar} this
31116 * @param {Number} newSize the new width or height
31121 * Fires when the splitter is moved
31122 * @param {Roo.bootstrap.SplitBar} this
31123 * @param {Number} newSize the new width or height
31127 * @event beforeresize
31128 * Fires before the splitter is dragged
31129 * @param {Roo.bootstrap.SplitBar} this
31131 "beforeresize" : true,
31133 "beforeapply" : true
31136 Roo.util.Observable.call(this);
31139 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31140 onStartProxyDrag : function(x, y){
31141 this.fireEvent("beforeresize", this);
31143 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
31145 o.enableDisplayMode("block");
31146 // all splitbars share the same overlay
31147 Roo.bootstrap.SplitBar.prototype.overlay = o;
31149 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31150 this.overlay.show();
31151 Roo.get(this.proxy).setDisplayed("block");
31152 var size = this.adapter.getElementSize(this);
31153 this.activeMinSize = this.getMinimumSize();;
31154 this.activeMaxSize = this.getMaximumSize();;
31155 var c1 = size - this.activeMinSize;
31156 var c2 = Math.max(this.activeMaxSize - size, 0);
31157 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31158 this.dd.resetConstraints();
31159 this.dd.setXConstraint(
31160 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
31161 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31163 this.dd.setYConstraint(0, 0);
31165 this.dd.resetConstraints();
31166 this.dd.setXConstraint(0, 0);
31167 this.dd.setYConstraint(
31168 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
31169 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31172 this.dragSpecs.startSize = size;
31173 this.dragSpecs.startPoint = [x, y];
31174 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31178 * @private Called after the drag operation by the DDProxy
31180 onEndProxyDrag : function(e){
31181 Roo.get(this.proxy).setDisplayed(false);
31182 var endPoint = Roo.lib.Event.getXY(e);
31184 this.overlay.hide();
31187 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31188 newSize = this.dragSpecs.startSize +
31189 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31190 endPoint[0] - this.dragSpecs.startPoint[0] :
31191 this.dragSpecs.startPoint[0] - endPoint[0]
31194 newSize = this.dragSpecs.startSize +
31195 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31196 endPoint[1] - this.dragSpecs.startPoint[1] :
31197 this.dragSpecs.startPoint[1] - endPoint[1]
31200 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31201 if(newSize != this.dragSpecs.startSize){
31202 if(this.fireEvent('beforeapply', this, newSize) !== false){
31203 this.adapter.setElementSize(this, newSize);
31204 this.fireEvent("moved", this, newSize);
31205 this.fireEvent("resize", this, newSize);
31211 * Get the adapter this SplitBar uses
31212 * @return The adapter object
31214 getAdapter : function(){
31215 return this.adapter;
31219 * Set the adapter this SplitBar uses
31220 * @param {Object} adapter A SplitBar adapter object
31222 setAdapter : function(adapter){
31223 this.adapter = adapter;
31224 this.adapter.init(this);
31228 * Gets the minimum size for the resizing element
31229 * @return {Number} The minimum size
31231 getMinimumSize : function(){
31232 return this.minSize;
31236 * Sets the minimum size for the resizing element
31237 * @param {Number} minSize The minimum size
31239 setMinimumSize : function(minSize){
31240 this.minSize = minSize;
31244 * Gets the maximum size for the resizing element
31245 * @return {Number} The maximum size
31247 getMaximumSize : function(){
31248 return this.maxSize;
31252 * Sets the maximum size for the resizing element
31253 * @param {Number} maxSize The maximum size
31255 setMaximumSize : function(maxSize){
31256 this.maxSize = maxSize;
31260 * Sets the initialize size for the resizing element
31261 * @param {Number} size The initial size
31263 setCurrentSize : function(size){
31264 var oldAnimate = this.animate;
31265 this.animate = false;
31266 this.adapter.setElementSize(this, size);
31267 this.animate = oldAnimate;
31271 * Destroy this splitbar.
31272 * @param {Boolean} removeEl True to remove the element
31274 destroy : function(removeEl){
31276 this.shim.remove();
31279 this.proxy.parentNode.removeChild(this.proxy);
31287 * @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.
31289 Roo.bootstrap.SplitBar.createProxy = function(dir){
31290 var proxy = new Roo.Element(document.createElement("div"));
31291 proxy.unselectable();
31292 var cls = 'roo-splitbar-proxy';
31293 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31294 document.body.appendChild(proxy.dom);
31299 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31300 * Default Adapter. It assumes the splitter and resizing element are not positioned
31301 * elements and only gets/sets the width of the element. Generally used for table based layouts.
31303 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31306 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31307 // do nothing for now
31308 init : function(s){
31312 * Called before drag operations to get the current size of the resizing element.
31313 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31315 getElementSize : function(s){
31316 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31317 return s.resizingEl.getWidth();
31319 return s.resizingEl.getHeight();
31324 * Called after drag operations to set the size of the resizing element.
31325 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31326 * @param {Number} newSize The new size to set
31327 * @param {Function} onComplete A function to be invoked when resizing is complete
31329 setElementSize : function(s, newSize, onComplete){
31330 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31332 s.resizingEl.setWidth(newSize);
31334 onComplete(s, newSize);
31337 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31342 s.resizingEl.setHeight(newSize);
31344 onComplete(s, newSize);
31347 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31354 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31355 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31356 * Adapter that moves the splitter element to align with the resized sizing element.
31357 * Used with an absolute positioned SplitBar.
31358 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31359 * document.body, make sure you assign an id to the body element.
31361 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31362 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31363 this.container = Roo.get(container);
31366 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31367 init : function(s){
31368 this.basic.init(s);
31371 getElementSize : function(s){
31372 return this.basic.getElementSize(s);
31375 setElementSize : function(s, newSize, onComplete){
31376 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31379 moveSplitter : function(s){
31380 var yes = Roo.bootstrap.SplitBar;
31381 switch(s.placement){
31383 s.el.setX(s.resizingEl.getRight());
31386 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31389 s.el.setY(s.resizingEl.getBottom());
31392 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31399 * Orientation constant - Create a vertical SplitBar
31403 Roo.bootstrap.SplitBar.VERTICAL = 1;
31406 * Orientation constant - Create a horizontal SplitBar
31410 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31413 * Placement constant - The resizing element is to the left of the splitter element
31417 Roo.bootstrap.SplitBar.LEFT = 1;
31420 * Placement constant - The resizing element is to the right of the splitter element
31424 Roo.bootstrap.SplitBar.RIGHT = 2;
31427 * Placement constant - The resizing element is positioned above the splitter element
31431 Roo.bootstrap.SplitBar.TOP = 3;
31434 * Placement constant - The resizing element is positioned under splitter element
31438 Roo.bootstrap.SplitBar.BOTTOM = 4;
31439 Roo.namespace("Roo.bootstrap.layout");/*
31441 * Ext JS Library 1.1.1
31442 * Copyright(c) 2006-2007, Ext JS, LLC.
31444 * Originally Released Under LGPL - original licence link has changed is not relivant.
31447 * <script type="text/javascript">
31451 * @class Roo.bootstrap.layout.Manager
31452 * @extends Roo.bootstrap.Component
31453 * Base class for layout managers.
31455 Roo.bootstrap.layout.Manager = function(config)
31457 Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31463 /** false to disable window resize monitoring @type Boolean */
31464 this.monitorWindowResize = true;
31469 * Fires when a layout is performed.
31470 * @param {Roo.LayoutManager} this
31474 * @event regionresized
31475 * Fires when the user resizes a region.
31476 * @param {Roo.LayoutRegion} region The resized region
31477 * @param {Number} newSize The new size (width for east/west, height for north/south)
31479 "regionresized" : true,
31481 * @event regioncollapsed
31482 * Fires when a region is collapsed.
31483 * @param {Roo.LayoutRegion} region The collapsed region
31485 "regioncollapsed" : true,
31487 * @event regionexpanded
31488 * Fires when a region is expanded.
31489 * @param {Roo.LayoutRegion} region The expanded region
31491 "regionexpanded" : true
31493 this.updating = false;
31496 this.el = Roo.get(config.el);
31502 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31507 monitorWindowResize : true,
31513 onRender : function(ct, position)
31516 this.el = Roo.get(ct);
31522 initEvents: function()
31526 // ie scrollbar fix
31527 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31528 document.body.scroll = "no";
31529 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31530 this.el.position('relative');
31532 this.id = this.el.id;
31533 this.el.addClass("roo-layout-container");
31534 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31535 if(this.el.dom != document.body ) {
31536 this.el.on('resize', this.layout,this);
31537 this.el.on('show', this.layout,this);
31543 * Returns true if this layout is currently being updated
31544 * @return {Boolean}
31546 isUpdating : function(){
31547 return this.updating;
31551 * Suspend the LayoutManager from doing auto-layouts while
31552 * making multiple add or remove calls
31554 beginUpdate : function(){
31555 this.updating = true;
31559 * Restore auto-layouts and optionally disable the manager from performing a layout
31560 * @param {Boolean} noLayout true to disable a layout update
31562 endUpdate : function(noLayout){
31563 this.updating = false;
31569 layout: function(){
31573 onRegionResized : function(region, newSize){
31574 this.fireEvent("regionresized", region, newSize);
31578 onRegionCollapsed : function(region){
31579 this.fireEvent("regioncollapsed", region);
31582 onRegionExpanded : function(region){
31583 this.fireEvent("regionexpanded", region);
31587 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31588 * performs box-model adjustments.
31589 * @return {Object} The size as an object {width: (the width), height: (the height)}
31591 getViewSize : function()
31594 if(this.el.dom != document.body){
31595 size = this.el.getSize();
31597 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31599 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31600 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31605 * Returns the Element this layout is bound to.
31606 * @return {Roo.Element}
31608 getEl : function(){
31613 * Returns the specified region.
31614 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31615 * @return {Roo.LayoutRegion}
31617 getRegion : function(target){
31618 return this.regions[target.toLowerCase()];
31621 onWindowResize : function(){
31622 if(this.monitorWindowResize){
31628 * Ext JS Library 1.1.1
31629 * Copyright(c) 2006-2007, Ext JS, LLC.
31631 * Originally Released Under LGPL - original licence link has changed is not relivant.
31634 * <script type="text/javascript">
31637 * @class Roo.bootstrap.layout.Border
31638 * @extends Roo.bootstrap.layout.Manager
31639 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31640 * please see: examples/bootstrap/nested.html<br><br>
31642 <b>The container the layout is rendered into can be either the body element or any other element.
31643 If it is not the body element, the container needs to either be an absolute positioned element,
31644 or you will need to add "position:relative" to the css of the container. You will also need to specify
31645 the container size if it is not the body element.</b>
31648 * Create a new Border
31649 * @param {Object} config Configuration options
31651 Roo.bootstrap.layout.Border = function(config){
31652 config = config || {};
31653 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31657 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31658 if(config[region]){
31659 config[region].region = region;
31660 this.addRegion(config[region]);
31666 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
31668 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31670 * Creates and adds a new region if it doesn't already exist.
31671 * @param {String} target The target region key (north, south, east, west or center).
31672 * @param {Object} config The regions config object
31673 * @return {BorderLayoutRegion} The new region
31675 addRegion : function(config)
31677 if(!this.regions[config.region]){
31678 var r = this.factory(config);
31679 this.bindRegion(r);
31681 return this.regions[config.region];
31685 bindRegion : function(r){
31686 this.regions[r.config.region] = r;
31688 r.on("visibilitychange", this.layout, this);
31689 r.on("paneladded", this.layout, this);
31690 r.on("panelremoved", this.layout, this);
31691 r.on("invalidated", this.layout, this);
31692 r.on("resized", this.onRegionResized, this);
31693 r.on("collapsed", this.onRegionCollapsed, this);
31694 r.on("expanded", this.onRegionExpanded, this);
31698 * Performs a layout update.
31700 layout : function()
31702 if(this.updating) {
31705 var size = this.getViewSize();
31706 var w = size.width;
31707 var h = size.height;
31712 //var x = 0, y = 0;
31714 var rs = this.regions;
31715 var north = rs["north"];
31716 var south = rs["south"];
31717 var west = rs["west"];
31718 var east = rs["east"];
31719 var center = rs["center"];
31720 //if(this.hideOnLayout){ // not supported anymore
31721 //c.el.setStyle("display", "none");
31723 if(north && north.isVisible()){
31724 var b = north.getBox();
31725 var m = north.getMargins();
31726 b.width = w - (m.left+m.right);
31729 centerY = b.height + b.y + m.bottom;
31730 centerH -= centerY;
31731 north.updateBox(this.safeBox(b));
31733 if(south && south.isVisible()){
31734 var b = south.getBox();
31735 var m = south.getMargins();
31736 b.width = w - (m.left+m.right);
31738 var totalHeight = (b.height + m.top + m.bottom);
31739 b.y = h - totalHeight + m.top;
31740 centerH -= totalHeight;
31741 south.updateBox(this.safeBox(b));
31743 if(west && west.isVisible()){
31744 var b = west.getBox();
31745 var m = west.getMargins();
31746 b.height = centerH - (m.top+m.bottom);
31748 b.y = centerY + m.top;
31749 var totalWidth = (b.width + m.left + m.right);
31750 centerX += totalWidth;
31751 centerW -= totalWidth;
31752 west.updateBox(this.safeBox(b));
31754 if(east && east.isVisible()){
31755 var b = east.getBox();
31756 var m = east.getMargins();
31757 b.height = centerH - (m.top+m.bottom);
31758 var totalWidth = (b.width + m.left + m.right);
31759 b.x = w - totalWidth + m.left;
31760 b.y = centerY + m.top;
31761 centerW -= totalWidth;
31762 east.updateBox(this.safeBox(b));
31765 var m = center.getMargins();
31767 x: centerX + m.left,
31768 y: centerY + m.top,
31769 width: centerW - (m.left+m.right),
31770 height: centerH - (m.top+m.bottom)
31772 //if(this.hideOnLayout){
31773 //center.el.setStyle("display", "block");
31775 center.updateBox(this.safeBox(centerBox));
31778 this.fireEvent("layout", this);
31782 safeBox : function(box){
31783 box.width = Math.max(0, box.width);
31784 box.height = Math.max(0, box.height);
31789 * Adds a ContentPanel (or subclass) to this layout.
31790 * @param {String} target The target region key (north, south, east, west or center).
31791 * @param {Roo.ContentPanel} panel The panel to add
31792 * @return {Roo.ContentPanel} The added panel
31794 add : function(target, panel){
31796 target = target.toLowerCase();
31797 return this.regions[target].add(panel);
31801 * Remove a ContentPanel (or subclass) to this layout.
31802 * @param {String} target The target region key (north, south, east, west or center).
31803 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31804 * @return {Roo.ContentPanel} The removed panel
31806 remove : function(target, panel){
31807 target = target.toLowerCase();
31808 return this.regions[target].remove(panel);
31812 * Searches all regions for a panel with the specified id
31813 * @param {String} panelId
31814 * @return {Roo.ContentPanel} The panel or null if it wasn't found
31816 findPanel : function(panelId){
31817 var rs = this.regions;
31818 for(var target in rs){
31819 if(typeof rs[target] != "function"){
31820 var p = rs[target].getPanel(panelId);
31830 * Searches all regions for a panel with the specified id and activates (shows) it.
31831 * @param {String/ContentPanel} panelId The panels id or the panel itself
31832 * @return {Roo.ContentPanel} The shown panel or null
31834 showPanel : function(panelId) {
31835 var rs = this.regions;
31836 for(var target in rs){
31837 var r = rs[target];
31838 if(typeof r != "function"){
31839 if(r.hasPanel(panelId)){
31840 return r.showPanel(panelId);
31848 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31849 * @param {Roo.state.Provider} provider (optional) An alternate state provider
31852 restoreState : function(provider){
31854 provider = Roo.state.Manager;
31856 var sm = new Roo.LayoutStateManager();
31857 sm.init(this, provider);
31863 * Adds a xtype elements to the layout.
31867 xtype : 'ContentPanel',
31874 xtype : 'NestedLayoutPanel',
31880 items : [ ... list of content panels or nested layout panels.. ]
31884 * @param {Object} cfg Xtype definition of item to add.
31886 addxtype : function(cfg)
31888 // basically accepts a pannel...
31889 // can accept a layout region..!?!?
31890 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31893 // theory? children can only be panels??
31895 //if (!cfg.xtype.match(/Panel$/)) {
31900 if (typeof(cfg.region) == 'undefined') {
31901 Roo.log("Failed to add Panel, region was not set");
31905 var region = cfg.region;
31911 xitems = cfg.items;
31918 case 'Content': // ContentPanel (el, cfg)
31919 case 'Scroll': // ContentPanel (el, cfg)
31921 cfg.autoCreate = true;
31922 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31924 // var el = this.el.createChild();
31925 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31928 this.add(region, ret);
31932 case 'TreePanel': // our new panel!
31933 cfg.el = this.el.createChild();
31934 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31935 this.add(region, ret);
31940 // create a new Layout (which is a Border Layout...
31942 var clayout = cfg.layout;
31943 clayout.el = this.el.createChild();
31944 clayout.items = clayout.items || [];
31948 // replace this exitems with the clayout ones..
31949 xitems = clayout.items;
31951 // force background off if it's in center...
31952 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31953 cfg.background = false;
31955 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
31958 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31959 //console.log('adding nested layout panel ' + cfg.toSource());
31960 this.add(region, ret);
31961 nb = {}; /// find first...
31966 // needs grid and region
31968 //var el = this.getRegion(region).el.createChild();
31970 *var el = this.el.createChild();
31971 // create the grid first...
31972 cfg.grid.container = el;
31973 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31976 if (region == 'center' && this.active ) {
31977 cfg.background = false;
31980 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31982 this.add(region, ret);
31984 if (cfg.background) {
31985 // render grid on panel activation (if panel background)
31986 ret.on('activate', function(gp) {
31987 if (!gp.grid.rendered) {
31988 // gp.grid.render(el);
31992 // cfg.grid.render(el);
31998 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31999 // it was the old xcomponent building that caused this before.
32000 // espeically if border is the top element in the tree.
32010 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32012 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32013 this.add(region, ret);
32017 throw "Can not add '" + cfg.xtype + "' to Border";
32023 this.beginUpdate();
32027 Roo.each(xitems, function(i) {
32028 region = nb && i.region ? i.region : false;
32030 var add = ret.addxtype(i);
32033 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32034 if (!i.background) {
32035 abn[region] = nb[region] ;
32042 // make the last non-background panel active..
32043 //if (nb) { Roo.log(abn); }
32046 for(var r in abn) {
32047 region = this.getRegion(r);
32049 // tried using nb[r], but it does not work..
32051 region.showPanel(abn[r]);
32062 factory : function(cfg)
32065 var validRegions = Roo.bootstrap.layout.Border.regions;
32067 var target = cfg.region;
32070 var r = Roo.bootstrap.layout;
32074 return new r.North(cfg);
32076 return new r.South(cfg);
32078 return new r.East(cfg);
32080 return new r.West(cfg);
32082 return new r.Center(cfg);
32084 throw 'Layout region "'+target+'" not supported.';
32091 * Ext JS Library 1.1.1
32092 * Copyright(c) 2006-2007, Ext JS, LLC.
32094 * Originally Released Under LGPL - original licence link has changed is not relivant.
32097 * <script type="text/javascript">
32101 * @class Roo.bootstrap.layout.Basic
32102 * @extends Roo.util.Observable
32103 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32104 * and does not have a titlebar, tabs or any other features. All it does is size and position
32105 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32106 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32107 * @cfg {string} region the region that it inhabits..
32108 * @cfg {bool} skipConfig skip config?
32112 Roo.bootstrap.layout.Basic = function(config){
32114 this.mgr = config.mgr;
32116 this.position = config.region;
32118 var skipConfig = config.skipConfig;
32122 * @scope Roo.BasicLayoutRegion
32126 * @event beforeremove
32127 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32128 * @param {Roo.LayoutRegion} this
32129 * @param {Roo.ContentPanel} panel The panel
32130 * @param {Object} e The cancel event object
32132 "beforeremove" : true,
32134 * @event invalidated
32135 * Fires when the layout for this region is changed.
32136 * @param {Roo.LayoutRegion} this
32138 "invalidated" : true,
32140 * @event visibilitychange
32141 * Fires when this region is shown or hidden
32142 * @param {Roo.LayoutRegion} this
32143 * @param {Boolean} visibility true or false
32145 "visibilitychange" : true,
32147 * @event paneladded
32148 * Fires when a panel is added.
32149 * @param {Roo.LayoutRegion} this
32150 * @param {Roo.ContentPanel} panel The panel
32152 "paneladded" : true,
32154 * @event panelremoved
32155 * Fires when a panel is removed.
32156 * @param {Roo.LayoutRegion} this
32157 * @param {Roo.ContentPanel} panel The panel
32159 "panelremoved" : true,
32161 * @event beforecollapse
32162 * Fires when this region before collapse.
32163 * @param {Roo.LayoutRegion} this
32165 "beforecollapse" : true,
32168 * Fires when this region is collapsed.
32169 * @param {Roo.LayoutRegion} this
32171 "collapsed" : true,
32174 * Fires when this region is expanded.
32175 * @param {Roo.LayoutRegion} this
32180 * Fires when this region is slid into view.
32181 * @param {Roo.LayoutRegion} this
32183 "slideshow" : true,
32186 * Fires when this region slides out of view.
32187 * @param {Roo.LayoutRegion} this
32189 "slidehide" : true,
32191 * @event panelactivated
32192 * Fires when a panel is activated.
32193 * @param {Roo.LayoutRegion} this
32194 * @param {Roo.ContentPanel} panel The activated panel
32196 "panelactivated" : true,
32199 * Fires when the user resizes this region.
32200 * @param {Roo.LayoutRegion} this
32201 * @param {Number} newSize The new size (width for east/west, height for north/south)
32205 /** A collection of panels in this region. @type Roo.util.MixedCollection */
32206 this.panels = new Roo.util.MixedCollection();
32207 this.panels.getKey = this.getPanelId.createDelegate(this);
32209 this.activePanel = null;
32210 // ensure listeners are added...
32212 if (config.listeners || config.events) {
32213 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32214 listeners : config.listeners || {},
32215 events : config.events || {}
32219 if(skipConfig !== true){
32220 this.applyConfig(config);
32224 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32226 getPanelId : function(p){
32230 applyConfig : function(config){
32231 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32232 this.config = config;
32237 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
32238 * the width, for horizontal (north, south) the height.
32239 * @param {Number} newSize The new width or height
32241 resizeTo : function(newSize){
32242 var el = this.el ? this.el :
32243 (this.activePanel ? this.activePanel.getEl() : null);
32245 switch(this.position){
32248 el.setWidth(newSize);
32249 this.fireEvent("resized", this, newSize);
32253 el.setHeight(newSize);
32254 this.fireEvent("resized", this, newSize);
32260 getBox : function(){
32261 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32264 getMargins : function(){
32265 return this.margins;
32268 updateBox : function(box){
32270 var el = this.activePanel.getEl();
32271 el.dom.style.left = box.x + "px";
32272 el.dom.style.top = box.y + "px";
32273 this.activePanel.setSize(box.width, box.height);
32277 * Returns the container element for this region.
32278 * @return {Roo.Element}
32280 getEl : function(){
32281 return this.activePanel;
32285 * Returns true if this region is currently visible.
32286 * @return {Boolean}
32288 isVisible : function(){
32289 return this.activePanel ? true : false;
32292 setActivePanel : function(panel){
32293 panel = this.getPanel(panel);
32294 if(this.activePanel && this.activePanel != panel){
32295 this.activePanel.setActiveState(false);
32296 this.activePanel.getEl().setLeftTop(-10000,-10000);
32298 this.activePanel = panel;
32299 panel.setActiveState(true);
32301 panel.setSize(this.box.width, this.box.height);
32303 this.fireEvent("panelactivated", this, panel);
32304 this.fireEvent("invalidated");
32308 * Show the specified panel.
32309 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32310 * @return {Roo.ContentPanel} The shown panel or null
32312 showPanel : function(panel){
32313 panel = this.getPanel(panel);
32315 this.setActivePanel(panel);
32321 * Get the active panel for this region.
32322 * @return {Roo.ContentPanel} The active panel or null
32324 getActivePanel : function(){
32325 return this.activePanel;
32329 * Add the passed ContentPanel(s)
32330 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32331 * @return {Roo.ContentPanel} The panel added (if only one was added)
32333 add : function(panel){
32334 if(arguments.length > 1){
32335 for(var i = 0, len = arguments.length; i < len; i++) {
32336 this.add(arguments[i]);
32340 if(this.hasPanel(panel)){
32341 this.showPanel(panel);
32344 var el = panel.getEl();
32345 if(el.dom.parentNode != this.mgr.el.dom){
32346 this.mgr.el.dom.appendChild(el.dom);
32348 if(panel.setRegion){
32349 panel.setRegion(this);
32351 this.panels.add(panel);
32352 el.setStyle("position", "absolute");
32353 if(!panel.background){
32354 this.setActivePanel(panel);
32355 if(this.config.initialSize && this.panels.getCount()==1){
32356 this.resizeTo(this.config.initialSize);
32359 this.fireEvent("paneladded", this, panel);
32364 * Returns true if the panel is in this region.
32365 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32366 * @return {Boolean}
32368 hasPanel : function(panel){
32369 if(typeof panel == "object"){ // must be panel obj
32370 panel = panel.getId();
32372 return this.getPanel(panel) ? true : false;
32376 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32377 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32378 * @param {Boolean} preservePanel Overrides the config preservePanel option
32379 * @return {Roo.ContentPanel} The panel that was removed
32381 remove : function(panel, preservePanel){
32382 panel = this.getPanel(panel);
32387 this.fireEvent("beforeremove", this, panel, e);
32388 if(e.cancel === true){
32391 var panelId = panel.getId();
32392 this.panels.removeKey(panelId);
32397 * Returns the panel specified or null if it's not in this region.
32398 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32399 * @return {Roo.ContentPanel}
32401 getPanel : function(id){
32402 if(typeof id == "object"){ // must be panel obj
32405 return this.panels.get(id);
32409 * Returns this regions position (north/south/east/west/center).
32412 getPosition: function(){
32413 return this.position;
32417 * Ext JS Library 1.1.1
32418 * Copyright(c) 2006-2007, Ext JS, LLC.
32420 * Originally Released Under LGPL - original licence link has changed is not relivant.
32423 * <script type="text/javascript">
32427 * @class Roo.bootstrap.layout.Region
32428 * @extends Roo.bootstrap.layout.Basic
32429 * This class represents a region in a layout manager.
32431 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32432 * @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})
32433 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
32434 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
32435 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
32436 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
32437 * @cfg {String} title The title for the region (overrides panel titles)
32438 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
32439 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32440 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
32441 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32442 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
32443 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32444 * the space available, similar to FireFox 1.5 tabs (defaults to false)
32445 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
32446 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
32447 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
32449 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
32450 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
32451 * @cfg {Boolean} disableTabTips True to disable tab tooltips
32452 * @cfg {Number} width For East/West panels
32453 * @cfg {Number} height For North/South panels
32454 * @cfg {Boolean} split To show the splitter
32455 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
32457 * @cfg {string} cls Extra CSS classes to add to region
32459 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32460 * @cfg {string} region the region that it inhabits..
32463 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
32464 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
32466 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
32467 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
32468 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
32470 Roo.bootstrap.layout.Region = function(config)
32472 this.applyConfig(config);
32474 var mgr = config.mgr;
32475 var pos = config.region;
32476 config.skipConfig = true;
32477 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32480 this.onRender(mgr.el);
32483 this.visible = true;
32484 this.collapsed = false;
32487 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32489 position: '', // set by wrapper (eg. north/south etc..)
32491 createBody : function(){
32492 /** This region's body element
32493 * @type Roo.Element */
32494 this.bodyEl = this.el.createChild({
32496 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32500 onRender: function(ctr, pos)
32502 var dh = Roo.DomHelper;
32503 /** This region's container element
32504 * @type Roo.Element */
32505 this.el = dh.append(ctr.dom, {
32507 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32509 /** This region's title element
32510 * @type Roo.Element */
32512 this.titleEl = dh.append(this.el.dom,
32515 unselectable: "on",
32516 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32518 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
32519 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32522 this.titleEl.enableDisplayMode();
32523 /** This region's title text element
32524 * @type HTMLElement */
32525 this.titleTextEl = this.titleEl.dom.firstChild;
32526 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32528 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32529 this.closeBtn.enableDisplayMode();
32530 this.closeBtn.on("click", this.closeClicked, this);
32531 this.closeBtn.hide();
32533 this.createBody(this.config);
32534 if(this.config.hideWhenEmpty){
32536 this.on("paneladded", this.validateVisibility, this);
32537 this.on("panelremoved", this.validateVisibility, this);
32539 if(this.autoScroll){
32540 this.bodyEl.setStyle("overflow", "auto");
32542 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32544 //if(c.titlebar !== false){
32545 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32546 this.titleEl.hide();
32548 this.titleEl.show();
32549 if(this.config.title){
32550 this.titleTextEl.innerHTML = this.config.title;
32554 if(this.config.collapsed){
32555 this.collapse(true);
32557 if(this.config.hidden){
32562 applyConfig : function(c)
32565 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32566 var dh = Roo.DomHelper;
32567 if(c.titlebar !== false){
32568 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32569 this.collapseBtn.on("click", this.collapse, this);
32570 this.collapseBtn.enableDisplayMode();
32572 if(c.showPin === true || this.showPin){
32573 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32574 this.stickBtn.enableDisplayMode();
32575 this.stickBtn.on("click", this.expand, this);
32576 this.stickBtn.hide();
32581 /** This region's collapsed element
32582 * @type Roo.Element */
32585 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32586 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32589 if(c.floatable !== false){
32590 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32591 this.collapsedEl.on("click", this.collapseClick, this);
32594 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32595 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32596 id: "message", unselectable: "on", style:{"float":"left"}});
32597 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32599 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32600 this.expandBtn.on("click", this.expand, this);
32604 if(this.collapseBtn){
32605 this.collapseBtn.setVisible(c.collapsible == true);
32608 this.cmargins = c.cmargins || this.cmargins ||
32609 (this.position == "west" || this.position == "east" ?
32610 {top: 0, left: 2, right:2, bottom: 0} :
32611 {top: 2, left: 0, right:0, bottom: 2});
32613 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32616 this.bottomTabs = c.tabPosition != "top";
32618 this.autoScroll = c.autoScroll || false;
32623 this.duration = c.duration || .30;
32624 this.slideDuration = c.slideDuration || .45;
32629 * Returns true if this region is currently visible.
32630 * @return {Boolean}
32632 isVisible : function(){
32633 return this.visible;
32637 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32638 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
32640 //setCollapsedTitle : function(title){
32641 // title = title || " ";
32642 // if(this.collapsedTitleTextEl){
32643 // this.collapsedTitleTextEl.innerHTML = title;
32647 getBox : function(){
32649 // if(!this.collapsed){
32650 b = this.el.getBox(false, true);
32652 // b = this.collapsedEl.getBox(false, true);
32657 getMargins : function(){
32658 return this.margins;
32659 //return this.collapsed ? this.cmargins : this.margins;
32662 highlight : function(){
32663 this.el.addClass("x-layout-panel-dragover");
32666 unhighlight : function(){
32667 this.el.removeClass("x-layout-panel-dragover");
32670 updateBox : function(box)
32673 if(!this.collapsed){
32674 this.el.dom.style.left = box.x + "px";
32675 this.el.dom.style.top = box.y + "px";
32676 this.updateBody(box.width, box.height);
32678 this.collapsedEl.dom.style.left = box.x + "px";
32679 this.collapsedEl.dom.style.top = box.y + "px";
32680 this.collapsedEl.setSize(box.width, box.height);
32683 this.tabs.autoSizeTabs();
32687 updateBody : function(w, h)
32690 this.el.setWidth(w);
32691 w -= this.el.getBorderWidth("rl");
32692 if(this.config.adjustments){
32693 w += this.config.adjustments[0];
32697 this.el.setHeight(h);
32698 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32699 h -= this.el.getBorderWidth("tb");
32700 if(this.config.adjustments){
32701 h += this.config.adjustments[1];
32703 this.bodyEl.setHeight(h);
32705 h = this.tabs.syncHeight(h);
32708 if(this.panelSize){
32709 w = w !== null ? w : this.panelSize.width;
32710 h = h !== null ? h : this.panelSize.height;
32712 if(this.activePanel){
32713 var el = this.activePanel.getEl();
32714 w = w !== null ? w : el.getWidth();
32715 h = h !== null ? h : el.getHeight();
32716 this.panelSize = {width: w, height: h};
32717 this.activePanel.setSize(w, h);
32719 if(Roo.isIE && this.tabs){
32720 this.tabs.el.repaint();
32725 * Returns the container element for this region.
32726 * @return {Roo.Element}
32728 getEl : function(){
32733 * Hides this region.
32736 //if(!this.collapsed){
32737 this.el.dom.style.left = "-2000px";
32740 // this.collapsedEl.dom.style.left = "-2000px";
32741 // this.collapsedEl.hide();
32743 this.visible = false;
32744 this.fireEvent("visibilitychange", this, false);
32748 * Shows this region if it was previously hidden.
32751 //if(!this.collapsed){
32754 // this.collapsedEl.show();
32756 this.visible = true;
32757 this.fireEvent("visibilitychange", this, true);
32760 closeClicked : function(){
32761 if(this.activePanel){
32762 this.remove(this.activePanel);
32766 collapseClick : function(e){
32768 e.stopPropagation();
32771 e.stopPropagation();
32777 * Collapses this region.
32778 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32781 collapse : function(skipAnim, skipCheck = false){
32782 if(this.collapsed) {
32786 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32788 this.collapsed = true;
32790 this.split.el.hide();
32792 if(this.config.animate && skipAnim !== true){
32793 this.fireEvent("invalidated", this);
32794 this.animateCollapse();
32796 this.el.setLocation(-20000,-20000);
32798 this.collapsedEl.show();
32799 this.fireEvent("collapsed", this);
32800 this.fireEvent("invalidated", this);
32806 animateCollapse : function(){
32811 * Expands this region if it was previously collapsed.
32812 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32813 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32816 expand : function(e, skipAnim){
32818 e.stopPropagation();
32820 if(!this.collapsed || this.el.hasActiveFx()) {
32824 this.afterSlideIn();
32827 this.collapsed = false;
32828 if(this.config.animate && skipAnim !== true){
32829 this.animateExpand();
32833 this.split.el.show();
32835 this.collapsedEl.setLocation(-2000,-2000);
32836 this.collapsedEl.hide();
32837 this.fireEvent("invalidated", this);
32838 this.fireEvent("expanded", this);
32842 animateExpand : function(){
32846 initTabs : function()
32848 this.bodyEl.setStyle("overflow", "hidden");
32849 var ts = new Roo.bootstrap.panel.Tabs({
32850 el: this.bodyEl.dom,
32851 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32852 disableTooltips: this.config.disableTabTips,
32853 toolbar : this.config.toolbar
32856 if(this.config.hideTabs){
32857 ts.stripWrap.setDisplayed(false);
32860 ts.resizeTabs = this.config.resizeTabs === true;
32861 ts.minTabWidth = this.config.minTabWidth || 40;
32862 ts.maxTabWidth = this.config.maxTabWidth || 250;
32863 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32864 ts.monitorResize = false;
32865 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32866 ts.bodyEl.addClass('roo-layout-tabs-body');
32867 this.panels.each(this.initPanelAsTab, this);
32870 initPanelAsTab : function(panel){
32871 var ti = this.tabs.addTab(
32873 panel.getTitle(), null,
32874 this.config.closeOnTab && panel.isClosable()
32876 if(panel.tabTip !== undefined){
32877 ti.setTooltip(panel.tabTip);
32879 ti.on("activate", function(){
32880 this.setActivePanel(panel);
32883 if(this.config.closeOnTab){
32884 ti.on("beforeclose", function(t, e){
32886 this.remove(panel);
32892 updatePanelTitle : function(panel, title)
32894 if(this.activePanel == panel){
32895 this.updateTitle(title);
32898 var ti = this.tabs.getTab(panel.getEl().id);
32900 if(panel.tabTip !== undefined){
32901 ti.setTooltip(panel.tabTip);
32906 updateTitle : function(title){
32907 if(this.titleTextEl && !this.config.title){
32908 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
32912 setActivePanel : function(panel)
32914 panel = this.getPanel(panel);
32915 if(this.activePanel && this.activePanel != panel){
32916 this.activePanel.setActiveState(false);
32918 this.activePanel = panel;
32919 panel.setActiveState(true);
32920 if(this.panelSize){
32921 panel.setSize(this.panelSize.width, this.panelSize.height);
32924 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32926 this.updateTitle(panel.getTitle());
32928 this.fireEvent("invalidated", this);
32930 this.fireEvent("panelactivated", this, panel);
32934 * Shows the specified panel.
32935 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32936 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32938 showPanel : function(panel)
32940 panel = this.getPanel(panel);
32943 var tab = this.tabs.getTab(panel.getEl().id);
32944 if(tab.isHidden()){
32945 this.tabs.unhideTab(tab.id);
32949 this.setActivePanel(panel);
32956 * Get the active panel for this region.
32957 * @return {Roo.ContentPanel} The active panel or null
32959 getActivePanel : function(){
32960 return this.activePanel;
32963 validateVisibility : function(){
32964 if(this.panels.getCount() < 1){
32965 this.updateTitle(" ");
32966 this.closeBtn.hide();
32969 if(!this.isVisible()){
32976 * Adds the passed ContentPanel(s) to this region.
32977 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32978 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32980 add : function(panel){
32981 if(arguments.length > 1){
32982 for(var i = 0, len = arguments.length; i < len; i++) {
32983 this.add(arguments[i]);
32987 if(this.hasPanel(panel)){
32988 this.showPanel(panel);
32991 panel.setRegion(this);
32992 this.panels.add(panel);
32993 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32994 this.bodyEl.dom.appendChild(panel.getEl().dom);
32995 if(panel.background !== true){
32996 this.setActivePanel(panel);
32998 this.fireEvent("paneladded", this, panel);
33004 this.initPanelAsTab(panel);
33008 if(panel.background !== true){
33009 this.tabs.activate(panel.getEl().id);
33011 this.fireEvent("paneladded", this, panel);
33016 * Hides the tab for the specified panel.
33017 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33019 hidePanel : function(panel){
33020 if(this.tabs && (panel = this.getPanel(panel))){
33021 this.tabs.hideTab(panel.getEl().id);
33026 * Unhides the tab for a previously hidden panel.
33027 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33029 unhidePanel : function(panel){
33030 if(this.tabs && (panel = this.getPanel(panel))){
33031 this.tabs.unhideTab(panel.getEl().id);
33035 clearPanels : function(){
33036 while(this.panels.getCount() > 0){
33037 this.remove(this.panels.first());
33042 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33043 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33044 * @param {Boolean} preservePanel Overrides the config preservePanel option
33045 * @return {Roo.ContentPanel} The panel that was removed
33047 remove : function(panel, preservePanel)
33049 panel = this.getPanel(panel);
33054 this.fireEvent("beforeremove", this, panel, e);
33055 if(e.cancel === true){
33058 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33059 var panelId = panel.getId();
33060 this.panels.removeKey(panelId);
33062 document.body.appendChild(panel.getEl().dom);
33065 this.tabs.removeTab(panel.getEl().id);
33066 }else if (!preservePanel){
33067 this.bodyEl.dom.removeChild(panel.getEl().dom);
33069 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33070 var p = this.panels.first();
33071 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33072 tempEl.appendChild(p.getEl().dom);
33073 this.bodyEl.update("");
33074 this.bodyEl.dom.appendChild(p.getEl().dom);
33076 this.updateTitle(p.getTitle());
33078 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33079 this.setActivePanel(p);
33081 panel.setRegion(null);
33082 if(this.activePanel == panel){
33083 this.activePanel = null;
33085 if(this.config.autoDestroy !== false && preservePanel !== true){
33086 try{panel.destroy();}catch(e){}
33088 this.fireEvent("panelremoved", this, panel);
33093 * Returns the TabPanel component used by this region
33094 * @return {Roo.TabPanel}
33096 getTabs : function(){
33100 createTool : function(parentEl, className){
33101 var btn = Roo.DomHelper.append(parentEl, {
33103 cls: "x-layout-tools-button",
33106 cls: "roo-layout-tools-button-inner " + className,
33110 btn.addClassOnOver("roo-layout-tools-button-over");
33115 * Ext JS Library 1.1.1
33116 * Copyright(c) 2006-2007, Ext JS, LLC.
33118 * Originally Released Under LGPL - original licence link has changed is not relivant.
33121 * <script type="text/javascript">
33127 * @class Roo.SplitLayoutRegion
33128 * @extends Roo.LayoutRegion
33129 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33131 Roo.bootstrap.layout.Split = function(config){
33132 this.cursor = config.cursor;
33133 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33136 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33138 splitTip : "Drag to resize.",
33139 collapsibleSplitTip : "Drag to resize. Double click to hide.",
33140 useSplitTips : false,
33142 applyConfig : function(config){
33143 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33146 onRender : function(ctr,pos) {
33148 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33149 if(!this.config.split){
33154 var splitEl = Roo.DomHelper.append(ctr.dom, {
33156 id: this.el.id + "-split",
33157 cls: "roo-layout-split roo-layout-split-"+this.position,
33160 /** The SplitBar for this region
33161 * @type Roo.SplitBar */
33162 // does not exist yet...
33163 Roo.log([this.position, this.orientation]);
33165 this.split = new Roo.bootstrap.SplitBar({
33166 dragElement : splitEl,
33167 resizingElement: this.el,
33168 orientation : this.orientation
33171 this.split.on("moved", this.onSplitMove, this);
33172 this.split.useShim = this.config.useShim === true;
33173 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33174 if(this.useSplitTips){
33175 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33177 //if(config.collapsible){
33178 // this.split.el.on("dblclick", this.collapse, this);
33181 if(typeof this.config.minSize != "undefined"){
33182 this.split.minSize = this.config.minSize;
33184 if(typeof this.config.maxSize != "undefined"){
33185 this.split.maxSize = this.config.maxSize;
33187 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33188 this.hideSplitter();
33193 getHMaxSize : function(){
33194 var cmax = this.config.maxSize || 10000;
33195 var center = this.mgr.getRegion("center");
33196 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33199 getVMaxSize : function(){
33200 var cmax = this.config.maxSize || 10000;
33201 var center = this.mgr.getRegion("center");
33202 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33205 onSplitMove : function(split, newSize){
33206 this.fireEvent("resized", this, newSize);
33210 * Returns the {@link Roo.SplitBar} for this region.
33211 * @return {Roo.SplitBar}
33213 getSplitBar : function(){
33218 this.hideSplitter();
33219 Roo.bootstrap.layout.Split.superclass.hide.call(this);
33222 hideSplitter : function(){
33224 this.split.el.setLocation(-2000,-2000);
33225 this.split.el.hide();
33231 this.split.el.show();
33233 Roo.bootstrap.layout.Split.superclass.show.call(this);
33236 beforeSlide: function(){
33237 if(Roo.isGecko){// firefox overflow auto bug workaround
33238 this.bodyEl.clip();
33240 this.tabs.bodyEl.clip();
33242 if(this.activePanel){
33243 this.activePanel.getEl().clip();
33245 if(this.activePanel.beforeSlide){
33246 this.activePanel.beforeSlide();
33252 afterSlide : function(){
33253 if(Roo.isGecko){// firefox overflow auto bug workaround
33254 this.bodyEl.unclip();
33256 this.tabs.bodyEl.unclip();
33258 if(this.activePanel){
33259 this.activePanel.getEl().unclip();
33260 if(this.activePanel.afterSlide){
33261 this.activePanel.afterSlide();
33267 initAutoHide : function(){
33268 if(this.autoHide !== false){
33269 if(!this.autoHideHd){
33270 var st = new Roo.util.DelayedTask(this.slideIn, this);
33271 this.autoHideHd = {
33272 "mouseout": function(e){
33273 if(!e.within(this.el, true)){
33277 "mouseover" : function(e){
33283 this.el.on(this.autoHideHd);
33287 clearAutoHide : function(){
33288 if(this.autoHide !== false){
33289 this.el.un("mouseout", this.autoHideHd.mouseout);
33290 this.el.un("mouseover", this.autoHideHd.mouseover);
33294 clearMonitor : function(){
33295 Roo.get(document).un("click", this.slideInIf, this);
33298 // these names are backwards but not changed for compat
33299 slideOut : function(){
33300 if(this.isSlid || this.el.hasActiveFx()){
33303 this.isSlid = true;
33304 if(this.collapseBtn){
33305 this.collapseBtn.hide();
33307 this.closeBtnState = this.closeBtn.getStyle('display');
33308 this.closeBtn.hide();
33310 this.stickBtn.show();
33313 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33314 this.beforeSlide();
33315 this.el.setStyle("z-index", 10001);
33316 this.el.slideIn(this.getSlideAnchor(), {
33317 callback: function(){
33319 this.initAutoHide();
33320 Roo.get(document).on("click", this.slideInIf, this);
33321 this.fireEvent("slideshow", this);
33328 afterSlideIn : function(){
33329 this.clearAutoHide();
33330 this.isSlid = false;
33331 this.clearMonitor();
33332 this.el.setStyle("z-index", "");
33333 if(this.collapseBtn){
33334 this.collapseBtn.show();
33336 this.closeBtn.setStyle('display', this.closeBtnState);
33338 this.stickBtn.hide();
33340 this.fireEvent("slidehide", this);
33343 slideIn : function(cb){
33344 if(!this.isSlid || this.el.hasActiveFx()){
33348 this.isSlid = false;
33349 this.beforeSlide();
33350 this.el.slideOut(this.getSlideAnchor(), {
33351 callback: function(){
33352 this.el.setLeftTop(-10000, -10000);
33354 this.afterSlideIn();
33362 slideInIf : function(e){
33363 if(!e.within(this.el)){
33368 animateCollapse : function(){
33369 this.beforeSlide();
33370 this.el.setStyle("z-index", 20000);
33371 var anchor = this.getSlideAnchor();
33372 this.el.slideOut(anchor, {
33373 callback : function(){
33374 this.el.setStyle("z-index", "");
33375 this.collapsedEl.slideIn(anchor, {duration:.3});
33377 this.el.setLocation(-10000,-10000);
33379 this.fireEvent("collapsed", this);
33386 animateExpand : function(){
33387 this.beforeSlide();
33388 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33389 this.el.setStyle("z-index", 20000);
33390 this.collapsedEl.hide({
33393 this.el.slideIn(this.getSlideAnchor(), {
33394 callback : function(){
33395 this.el.setStyle("z-index", "");
33398 this.split.el.show();
33400 this.fireEvent("invalidated", this);
33401 this.fireEvent("expanded", this);
33429 getAnchor : function(){
33430 return this.anchors[this.position];
33433 getCollapseAnchor : function(){
33434 return this.canchors[this.position];
33437 getSlideAnchor : function(){
33438 return this.sanchors[this.position];
33441 getAlignAdj : function(){
33442 var cm = this.cmargins;
33443 switch(this.position){
33459 getExpandAdj : function(){
33460 var c = this.collapsedEl, cm = this.cmargins;
33461 switch(this.position){
33463 return [-(cm.right+c.getWidth()+cm.left), 0];
33466 return [cm.right+c.getWidth()+cm.left, 0];
33469 return [0, -(cm.top+cm.bottom+c.getHeight())];
33472 return [0, cm.top+cm.bottom+c.getHeight()];
33478 * Ext JS Library 1.1.1
33479 * Copyright(c) 2006-2007, Ext JS, LLC.
33481 * Originally Released Under LGPL - original licence link has changed is not relivant.
33484 * <script type="text/javascript">
33487 * These classes are private internal classes
33489 Roo.bootstrap.layout.Center = function(config){
33490 config.region = "center";
33491 Roo.bootstrap.layout.Region.call(this, config);
33492 this.visible = true;
33493 this.minWidth = config.minWidth || 20;
33494 this.minHeight = config.minHeight || 20;
33497 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33499 // center panel can't be hidden
33503 // center panel can't be hidden
33506 getMinWidth: function(){
33507 return this.minWidth;
33510 getMinHeight: function(){
33511 return this.minHeight;
33524 Roo.bootstrap.layout.North = function(config)
33526 config.region = 'north';
33527 config.cursor = 'n-resize';
33529 Roo.bootstrap.layout.Split.call(this, config);
33533 this.split.placement = Roo.bootstrap.SplitBar.TOP;
33534 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33535 this.split.el.addClass("roo-layout-split-v");
33537 var size = config.initialSize || config.height;
33538 if(typeof size != "undefined"){
33539 this.el.setHeight(size);
33542 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33544 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33548 getBox : function(){
33549 if(this.collapsed){
33550 return this.collapsedEl.getBox();
33552 var box = this.el.getBox();
33554 box.height += this.split.el.getHeight();
33559 updateBox : function(box){
33560 if(this.split && !this.collapsed){
33561 box.height -= this.split.el.getHeight();
33562 this.split.el.setLeft(box.x);
33563 this.split.el.setTop(box.y+box.height);
33564 this.split.el.setWidth(box.width);
33566 if(this.collapsed){
33567 this.updateBody(box.width, null);
33569 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33577 Roo.bootstrap.layout.South = function(config){
33578 config.region = 'south';
33579 config.cursor = 's-resize';
33580 Roo.bootstrap.layout.Split.call(this, config);
33582 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33583 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33584 this.split.el.addClass("roo-layout-split-v");
33586 var size = config.initialSize || config.height;
33587 if(typeof size != "undefined"){
33588 this.el.setHeight(size);
33592 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33593 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33594 getBox : function(){
33595 if(this.collapsed){
33596 return this.collapsedEl.getBox();
33598 var box = this.el.getBox();
33600 var sh = this.split.el.getHeight();
33607 updateBox : function(box){
33608 if(this.split && !this.collapsed){
33609 var sh = this.split.el.getHeight();
33612 this.split.el.setLeft(box.x);
33613 this.split.el.setTop(box.y-sh);
33614 this.split.el.setWidth(box.width);
33616 if(this.collapsed){
33617 this.updateBody(box.width, null);
33619 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33623 Roo.bootstrap.layout.East = function(config){
33624 config.region = "east";
33625 config.cursor = "e-resize";
33626 Roo.bootstrap.layout.Split.call(this, config);
33628 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33629 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33630 this.split.el.addClass("roo-layout-split-h");
33632 var size = config.initialSize || config.width;
33633 if(typeof size != "undefined"){
33634 this.el.setWidth(size);
33637 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33638 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33639 getBox : function(){
33640 if(this.collapsed){
33641 return this.collapsedEl.getBox();
33643 var box = this.el.getBox();
33645 var sw = this.split.el.getWidth();
33652 updateBox : function(box){
33653 if(this.split && !this.collapsed){
33654 var sw = this.split.el.getWidth();
33656 this.split.el.setLeft(box.x);
33657 this.split.el.setTop(box.y);
33658 this.split.el.setHeight(box.height);
33661 if(this.collapsed){
33662 this.updateBody(null, box.height);
33664 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33668 Roo.bootstrap.layout.West = function(config){
33669 config.region = "west";
33670 config.cursor = "w-resize";
33672 Roo.bootstrap.layout.Split.call(this, config);
33674 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33675 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33676 this.split.el.addClass("roo-layout-split-h");
33680 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33681 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33683 onRender: function(ctr, pos)
33685 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33686 var size = this.config.initialSize || this.config.width;
33687 if(typeof size != "undefined"){
33688 this.el.setWidth(size);
33692 getBox : function(){
33693 if(this.collapsed){
33694 return this.collapsedEl.getBox();
33696 var box = this.el.getBox();
33698 box.width += this.split.el.getWidth();
33703 updateBox : function(box){
33704 if(this.split && !this.collapsed){
33705 var sw = this.split.el.getWidth();
33707 this.split.el.setLeft(box.x+box.width);
33708 this.split.el.setTop(box.y);
33709 this.split.el.setHeight(box.height);
33711 if(this.collapsed){
33712 this.updateBody(null, box.height);
33714 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33717 Roo.namespace("Roo.bootstrap.panel");/*
33719 * Ext JS Library 1.1.1
33720 * Copyright(c) 2006-2007, Ext JS, LLC.
33722 * Originally Released Under LGPL - original licence link has changed is not relivant.
33725 * <script type="text/javascript">
33728 * @class Roo.ContentPanel
33729 * @extends Roo.util.Observable
33730 * A basic ContentPanel element.
33731 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
33732 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
33733 * @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
33734 * @cfg {Boolean} closable True if the panel can be closed/removed
33735 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
33736 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33737 * @cfg {Toolbar} toolbar A toolbar for this panel
33738 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
33739 * @cfg {String} title The title for this panel
33740 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33741 * @cfg {String} url Calls {@link #setUrl} with this value
33742 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33743 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
33744 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
33745 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
33748 * Create a new ContentPanel.
33749 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33750 * @param {String/Object} config A string to set only the title or a config object
33751 * @param {String} content (optional) Set the HTML content for this panel
33752 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33754 Roo.bootstrap.panel.Content = function( config){
33756 var el = config.el;
33757 var content = config.content;
33759 if(config.autoCreate){ // xtype is available if this is called from factory
33762 this.el = Roo.get(el);
33763 if(!this.el && config && config.autoCreate){
33764 if(typeof config.autoCreate == "object"){
33765 if(!config.autoCreate.id){
33766 config.autoCreate.id = config.id||el;
33768 this.el = Roo.DomHelper.append(document.body,
33769 config.autoCreate, true);
33771 var elcfg = { tag: "div",
33772 cls: "roo-layout-inactive-content",
33776 elcfg.html = config.html;
33780 this.el = Roo.DomHelper.append(document.body, elcfg , true);
33783 this.closable = false;
33784 this.loaded = false;
33785 this.active = false;
33788 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33790 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33792 this.wrapEl = this.el.wrap();
33794 if (config.toolbar.items) {
33795 ti = config.toolbar.items ;
33796 delete config.toolbar.items ;
33800 this.toolbar.render(this.wrapEl, 'before');
33801 for(var i =0;i < ti.length;i++) {
33802 // Roo.log(['add child', items[i]]);
33803 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33805 this.toolbar.items = nitems;
33806 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33807 delete config.toolbar;
33811 // xtype created footer. - not sure if will work as we normally have to render first..
33812 if (this.footer && !this.footer.el && this.footer.xtype) {
33813 if (!this.wrapEl) {
33814 this.wrapEl = this.el.wrap();
33817 this.footer.container = this.wrapEl.createChild();
33819 this.footer = Roo.factory(this.footer, Roo);
33824 if(typeof config == "string"){
33825 this.title = config;
33827 Roo.apply(this, config);
33831 this.resizeEl = Roo.get(this.resizeEl, true);
33833 this.resizeEl = this.el;
33835 // handle view.xtype
33843 * Fires when this panel is activated.
33844 * @param {Roo.ContentPanel} this
33848 * @event deactivate
33849 * Fires when this panel is activated.
33850 * @param {Roo.ContentPanel} this
33852 "deactivate" : true,
33856 * Fires when this panel is resized if fitToFrame is true.
33857 * @param {Roo.ContentPanel} this
33858 * @param {Number} width The width after any component adjustments
33859 * @param {Number} height The height after any component adjustments
33865 * Fires when this tab is created
33866 * @param {Roo.ContentPanel} this
33877 if(this.autoScroll){
33878 this.resizeEl.setStyle("overflow", "auto");
33880 // fix randome scrolling
33881 //this.el.on('scroll', function() {
33882 // Roo.log('fix random scolling');
33883 // this.scrollTo('top',0);
33886 content = content || this.content;
33888 this.setContent(content);
33890 if(config && config.url){
33891 this.setUrl(this.url, this.params, this.loadOnce);
33896 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33898 if (this.view && typeof(this.view.xtype) != 'undefined') {
33899 this.view.el = this.el.appendChild(document.createElement("div"));
33900 this.view = Roo.factory(this.view);
33901 this.view.render && this.view.render(false, '');
33905 this.fireEvent('render', this);
33908 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33910 setRegion : function(region){
33911 this.region = region;
33913 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33915 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33920 * Returns the toolbar for this Panel if one was configured.
33921 * @return {Roo.Toolbar}
33923 getToolbar : function(){
33924 return this.toolbar;
33927 setActiveState : function(active){
33928 this.active = active;
33930 this.fireEvent("deactivate", this);
33932 this.fireEvent("activate", this);
33936 * Updates this panel's element
33937 * @param {String} content The new content
33938 * @param {Boolean} loadScripts (optional) true to look for and process scripts
33940 setContent : function(content, loadScripts){
33941 this.el.update(content, loadScripts);
33944 ignoreResize : function(w, h){
33945 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33948 this.lastSize = {width: w, height: h};
33953 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33954 * @return {Roo.UpdateManager} The UpdateManager
33956 getUpdateManager : function(){
33957 return this.el.getUpdateManager();
33960 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33961 * @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:
33964 url: "your-url.php",
33965 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33966 callback: yourFunction,
33967 scope: yourObject, //(optional scope)
33970 text: "Loading...",
33975 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33976 * 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.
33977 * @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}
33978 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33979 * @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.
33980 * @return {Roo.ContentPanel} this
33983 var um = this.el.getUpdateManager();
33984 um.update.apply(um, arguments);
33990 * 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.
33991 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33992 * @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)
33993 * @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)
33994 * @return {Roo.UpdateManager} The UpdateManager
33996 setUrl : function(url, params, loadOnce){
33997 if(this.refreshDelegate){
33998 this.removeListener("activate", this.refreshDelegate);
34000 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34001 this.on("activate", this.refreshDelegate);
34002 return this.el.getUpdateManager();
34005 _handleRefresh : function(url, params, loadOnce){
34006 if(!loadOnce || !this.loaded){
34007 var updater = this.el.getUpdateManager();
34008 updater.update(url, params, this._setLoaded.createDelegate(this));
34012 _setLoaded : function(){
34013 this.loaded = true;
34017 * Returns this panel's id
34020 getId : function(){
34025 * Returns this panel's element - used by regiosn to add.
34026 * @return {Roo.Element}
34028 getEl : function(){
34029 return this.wrapEl || this.el;
34034 adjustForComponents : function(width, height)
34036 //Roo.log('adjustForComponents ');
34037 if(this.resizeEl != this.el){
34038 width -= this.el.getFrameWidth('lr');
34039 height -= this.el.getFrameWidth('tb');
34042 var te = this.toolbar.getEl();
34043 height -= te.getHeight();
34044 te.setWidth(width);
34047 var te = this.footer.getEl();
34048 Roo.log("footer:" + te.getHeight());
34050 height -= te.getHeight();
34051 te.setWidth(width);
34055 if(this.adjustments){
34056 width += this.adjustments[0];
34057 height += this.adjustments[1];
34059 return {"width": width, "height": height};
34062 setSize : function(width, height){
34063 if(this.fitToFrame && !this.ignoreResize(width, height)){
34064 if(this.fitContainer && this.resizeEl != this.el){
34065 this.el.setSize(width, height);
34067 var size = this.adjustForComponents(width, height);
34068 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34069 this.fireEvent('resize', this, size.width, size.height);
34074 * Returns this panel's title
34077 getTitle : function(){
34082 * Set this panel's title
34083 * @param {String} title
34085 setTitle : function(title){
34086 this.title = title;
34088 this.region.updatePanelTitle(this, title);
34093 * Returns true is this panel was configured to be closable
34094 * @return {Boolean}
34096 isClosable : function(){
34097 return this.closable;
34100 beforeSlide : function(){
34102 this.resizeEl.clip();
34105 afterSlide : function(){
34107 this.resizeEl.unclip();
34111 * Force a content refresh from the URL specified in the {@link #setUrl} method.
34112 * Will fail silently if the {@link #setUrl} method has not been called.
34113 * This does not activate the panel, just updates its content.
34115 refresh : function(){
34116 if(this.refreshDelegate){
34117 this.loaded = false;
34118 this.refreshDelegate();
34123 * Destroys this panel
34125 destroy : function(){
34126 this.el.removeAllListeners();
34127 var tempEl = document.createElement("span");
34128 tempEl.appendChild(this.el.dom);
34129 tempEl.innerHTML = "";
34135 * form - if the content panel contains a form - this is a reference to it.
34136 * @type {Roo.form.Form}
34140 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34141 * This contains a reference to it.
34147 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34157 * @param {Object} cfg Xtype definition of item to add.
34161 getChildContainer: function () {
34162 return this.getEl();
34167 var ret = new Roo.factory(cfg);
34172 if (cfg.xtype.match(/^Form$/)) {
34175 //if (this.footer) {
34176 // el = this.footer.container.insertSibling(false, 'before');
34178 el = this.el.createChild();
34181 this.form = new Roo.form.Form(cfg);
34184 if ( this.form.allItems.length) {
34185 this.form.render(el.dom);
34189 // should only have one of theses..
34190 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34191 // views.. should not be just added - used named prop 'view''
34193 cfg.el = this.el.appendChild(document.createElement("div"));
34196 var ret = new Roo.factory(cfg);
34198 ret.render && ret.render(false, ''); // render blank..
34208 * @class Roo.bootstrap.panel.Grid
34209 * @extends Roo.bootstrap.panel.Content
34211 * Create a new GridPanel.
34212 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34213 * @param {Object} config A the config object
34219 Roo.bootstrap.panel.Grid = function(config)
34223 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34224 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34226 config.el = this.wrapper;
34227 //this.el = this.wrapper;
34229 if (config.container) {
34230 // ctor'ed from a Border/panel.grid
34233 this.wrapper.setStyle("overflow", "hidden");
34234 this.wrapper.addClass('roo-grid-container');
34239 if(config.toolbar){
34240 var tool_el = this.wrapper.createChild();
34241 this.toolbar = Roo.factory(config.toolbar);
34243 if (config.toolbar.items) {
34244 ti = config.toolbar.items ;
34245 delete config.toolbar.items ;
34249 this.toolbar.render(tool_el);
34250 for(var i =0;i < ti.length;i++) {
34251 // Roo.log(['add child', items[i]]);
34252 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34254 this.toolbar.items = nitems;
34256 delete config.toolbar;
34259 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34260 config.grid.scrollBody = true;;
34261 config.grid.monitorWindowResize = false; // turn off autosizing
34262 config.grid.autoHeight = false;
34263 config.grid.autoWidth = false;
34265 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34267 if (config.background) {
34268 // render grid on panel activation (if panel background)
34269 this.on('activate', function(gp) {
34270 if (!gp.grid.rendered) {
34271 gp.grid.render(el);
34272 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34278 this.grid.render(this.wrapper);
34279 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34282 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34283 // ??? needed ??? config.el = this.wrapper;
34288 // xtype created footer. - not sure if will work as we normally have to render first..
34289 if (this.footer && !this.footer.el && this.footer.xtype) {
34291 var ctr = this.grid.getView().getFooterPanel(true);
34292 this.footer.dataSource = this.grid.dataSource;
34293 this.footer = Roo.factory(this.footer, Roo);
34294 this.footer.render(ctr);
34304 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34305 getId : function(){
34306 return this.grid.id;
34310 * Returns the grid for this panel
34311 * @return {Roo.bootstrap.Table}
34313 getGrid : function(){
34317 setSize : function(width, height){
34318 if(!this.ignoreResize(width, height)){
34319 var grid = this.grid;
34320 var size = this.adjustForComponents(width, height);
34321 var gridel = grid.getGridEl();
34322 gridel.setSize(size.width, size.height);
34324 var thd = grid.getGridEl().select('thead',true).first();
34325 var tbd = grid.getGridEl().select('tbody', true).first();
34327 tbd.setSize(width, height - thd.getHeight());
34336 beforeSlide : function(){
34337 this.grid.getView().scroller.clip();
34340 afterSlide : function(){
34341 this.grid.getView().scroller.unclip();
34344 destroy : function(){
34345 this.grid.destroy();
34347 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
34352 * @class Roo.bootstrap.panel.Nest
34353 * @extends Roo.bootstrap.panel.Content
34355 * Create a new Panel, that can contain a layout.Border.
34358 * @param {Roo.BorderLayout} layout The layout for this panel
34359 * @param {String/Object} config A string to set only the title or a config object
34361 Roo.bootstrap.panel.Nest = function(config)
34363 // construct with only one argument..
34364 /* FIXME - implement nicer consturctors
34365 if (layout.layout) {
34367 layout = config.layout;
34368 delete config.layout;
34370 if (layout.xtype && !layout.getEl) {
34371 // then layout needs constructing..
34372 layout = Roo.factory(layout, Roo);
34376 config.el = config.layout.getEl();
34378 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34380 config.layout.monitorWindowResize = false; // turn off autosizing
34381 this.layout = config.layout;
34382 this.layout.getEl().addClass("roo-layout-nested-layout");
34389 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34391 setSize : function(width, height){
34392 if(!this.ignoreResize(width, height)){
34393 var size = this.adjustForComponents(width, height);
34394 var el = this.layout.getEl();
34395 el.setSize(size.width, size.height);
34396 var touch = el.dom.offsetWidth;
34397 this.layout.layout();
34398 // ie requires a double layout on the first pass
34399 if(Roo.isIE && !this.initialized){
34400 this.initialized = true;
34401 this.layout.layout();
34406 // activate all subpanels if not currently active..
34408 setActiveState : function(active){
34409 this.active = active;
34411 this.fireEvent("deactivate", this);
34415 this.fireEvent("activate", this);
34416 // not sure if this should happen before or after..
34417 if (!this.layout) {
34418 return; // should not happen..
34421 for (var r in this.layout.regions) {
34422 reg = this.layout.getRegion(r);
34423 if (reg.getActivePanel()) {
34424 //reg.showPanel(reg.getActivePanel()); // force it to activate..
34425 reg.setActivePanel(reg.getActivePanel());
34428 if (!reg.panels.length) {
34431 reg.showPanel(reg.getPanel(0));
34440 * Returns the nested BorderLayout for this panel
34441 * @return {Roo.BorderLayout}
34443 getLayout : function(){
34444 return this.layout;
34448 * Adds a xtype elements to the layout of the nested panel
34452 xtype : 'ContentPanel',
34459 xtype : 'NestedLayoutPanel',
34465 items : [ ... list of content panels or nested layout panels.. ]
34469 * @param {Object} cfg Xtype definition of item to add.
34471 addxtype : function(cfg) {
34472 return this.layout.addxtype(cfg);
34477 * Ext JS Library 1.1.1
34478 * Copyright(c) 2006-2007, Ext JS, LLC.
34480 * Originally Released Under LGPL - original licence link has changed is not relivant.
34483 * <script type="text/javascript">
34486 * @class Roo.TabPanel
34487 * @extends Roo.util.Observable
34488 * A lightweight tab container.
34492 // basic tabs 1, built from existing content
34493 var tabs = new Roo.TabPanel("tabs1");
34494 tabs.addTab("script", "View Script");
34495 tabs.addTab("markup", "View Markup");
34496 tabs.activate("script");
34498 // more advanced tabs, built from javascript
34499 var jtabs = new Roo.TabPanel("jtabs");
34500 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34502 // set up the UpdateManager
34503 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34504 var updater = tab2.getUpdateManager();
34505 updater.setDefaultUrl("ajax1.htm");
34506 tab2.on('activate', updater.refresh, updater, true);
34508 // Use setUrl for Ajax loading
34509 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34510 tab3.setUrl("ajax2.htm", null, true);
34513 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34516 jtabs.activate("jtabs-1");
34519 * Create a new TabPanel.
34520 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34521 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34523 Roo.bootstrap.panel.Tabs = function(config){
34525 * The container element for this TabPanel.
34526 * @type Roo.Element
34528 this.el = Roo.get(config.el);
34531 if(typeof config == "boolean"){
34532 this.tabPosition = config ? "bottom" : "top";
34534 Roo.apply(this, config);
34538 if(this.tabPosition == "bottom"){
34539 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34540 this.el.addClass("roo-tabs-bottom");
34542 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34543 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34544 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34546 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34548 if(this.tabPosition != "bottom"){
34549 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34550 * @type Roo.Element
34552 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34553 this.el.addClass("roo-tabs-top");
34557 this.bodyEl.setStyle("position", "relative");
34559 this.active = null;
34560 this.activateDelegate = this.activate.createDelegate(this);
34565 * Fires when the active tab changes
34566 * @param {Roo.TabPanel} this
34567 * @param {Roo.TabPanelItem} activePanel The new active tab
34571 * @event beforetabchange
34572 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34573 * @param {Roo.TabPanel} this
34574 * @param {Object} e Set cancel to true on this object to cancel the tab change
34575 * @param {Roo.TabPanelItem} tab The tab being changed to
34577 "beforetabchange" : true
34580 Roo.EventManager.onWindowResize(this.onResize, this);
34581 this.cpad = this.el.getPadding("lr");
34582 this.hiddenCount = 0;
34585 // toolbar on the tabbar support...
34586 if (this.toolbar) {
34587 alert("no toolbar support yet");
34588 this.toolbar = false;
34590 var tcfg = this.toolbar;
34591 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
34592 this.toolbar = new Roo.Toolbar(tcfg);
34593 if (Roo.isSafari) {
34594 var tbl = tcfg.container.child('table', true);
34595 tbl.setAttribute('width', '100%');
34603 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34606 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34608 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34610 tabPosition : "top",
34612 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34614 currentTabWidth : 0,
34616 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34620 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34624 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34626 preferredTabWidth : 175,
34628 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34630 resizeTabs : false,
34632 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34634 monitorResize : true,
34636 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
34641 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34642 * @param {String} id The id of the div to use <b>or create</b>
34643 * @param {String} text The text for the tab
34644 * @param {String} content (optional) Content to put in the TabPanelItem body
34645 * @param {Boolean} closable (optional) True to create a close icon on the tab
34646 * @return {Roo.TabPanelItem} The created TabPanelItem
34648 addTab : function(id, text, content, closable)
34650 var item = new Roo.bootstrap.panel.TabItem({
34654 closable : closable
34656 this.addTabItem(item);
34658 item.setContent(content);
34664 * Returns the {@link Roo.TabPanelItem} with the specified id/index
34665 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34666 * @return {Roo.TabPanelItem}
34668 getTab : function(id){
34669 return this.items[id];
34673 * Hides the {@link Roo.TabPanelItem} with the specified id/index
34674 * @param {String/Number} id The id or index of the TabPanelItem to hide.
34676 hideTab : function(id){
34677 var t = this.items[id];
34680 this.hiddenCount++;
34681 this.autoSizeTabs();
34686 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34687 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34689 unhideTab : function(id){
34690 var t = this.items[id];
34692 t.setHidden(false);
34693 this.hiddenCount--;
34694 this.autoSizeTabs();
34699 * Adds an existing {@link Roo.TabPanelItem}.
34700 * @param {Roo.TabPanelItem} item The TabPanelItem to add
34702 addTabItem : function(item){
34703 this.items[item.id] = item;
34704 this.items.push(item);
34705 // if(this.resizeTabs){
34706 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34707 // this.autoSizeTabs();
34709 // item.autoSize();
34714 * Removes a {@link Roo.TabPanelItem}.
34715 * @param {String/Number} id The id or index of the TabPanelItem to remove.
34717 removeTab : function(id){
34718 var items = this.items;
34719 var tab = items[id];
34720 if(!tab) { return; }
34721 var index = items.indexOf(tab);
34722 if(this.active == tab && items.length > 1){
34723 var newTab = this.getNextAvailable(index);
34728 this.stripEl.dom.removeChild(tab.pnode.dom);
34729 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34730 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34732 items.splice(index, 1);
34733 delete this.items[tab.id];
34734 tab.fireEvent("close", tab);
34735 tab.purgeListeners();
34736 this.autoSizeTabs();
34739 getNextAvailable : function(start){
34740 var items = this.items;
34742 // look for a next tab that will slide over to
34743 // replace the one being removed
34744 while(index < items.length){
34745 var item = items[++index];
34746 if(item && !item.isHidden()){
34750 // if one isn't found select the previous tab (on the left)
34753 var item = items[--index];
34754 if(item && !item.isHidden()){
34762 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34763 * @param {String/Number} id The id or index of the TabPanelItem to disable.
34765 disableTab : function(id){
34766 var tab = this.items[id];
34767 if(tab && this.active != tab){
34773 * Enables a {@link Roo.TabPanelItem} that is disabled.
34774 * @param {String/Number} id The id or index of the TabPanelItem to enable.
34776 enableTab : function(id){
34777 var tab = this.items[id];
34782 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34783 * @param {String/Number} id The id or index of the TabPanelItem to activate.
34784 * @return {Roo.TabPanelItem} The TabPanelItem.
34786 activate : function(id){
34787 var tab = this.items[id];
34791 if(tab == this.active || tab.disabled){
34795 this.fireEvent("beforetabchange", this, e, tab);
34796 if(e.cancel !== true && !tab.disabled){
34798 this.active.hide();
34800 this.active = this.items[id];
34801 this.active.show();
34802 this.fireEvent("tabchange", this, this.active);
34808 * Gets the active {@link Roo.TabPanelItem}.
34809 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34811 getActiveTab : function(){
34812 return this.active;
34816 * Updates the tab body element to fit the height of the container element
34817 * for overflow scrolling
34818 * @param {Number} targetHeight (optional) Override the starting height from the elements height
34820 syncHeight : function(targetHeight){
34821 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34822 var bm = this.bodyEl.getMargins();
34823 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34824 this.bodyEl.setHeight(newHeight);
34828 onResize : function(){
34829 if(this.monitorResize){
34830 this.autoSizeTabs();
34835 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34837 beginUpdate : function(){
34838 this.updating = true;
34842 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34844 endUpdate : function(){
34845 this.updating = false;
34846 this.autoSizeTabs();
34850 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34852 autoSizeTabs : function(){
34853 var count = this.items.length;
34854 var vcount = count - this.hiddenCount;
34855 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34858 var w = Math.max(this.el.getWidth() - this.cpad, 10);
34859 var availWidth = Math.floor(w / vcount);
34860 var b = this.stripBody;
34861 if(b.getWidth() > w){
34862 var tabs = this.items;
34863 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34864 if(availWidth < this.minTabWidth){
34865 /*if(!this.sleft){ // incomplete scrolling code
34866 this.createScrollButtons();
34869 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34872 if(this.currentTabWidth < this.preferredTabWidth){
34873 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34879 * Returns the number of tabs in this TabPanel.
34882 getCount : function(){
34883 return this.items.length;
34887 * Resizes all the tabs to the passed width
34888 * @param {Number} The new width
34890 setTabWidth : function(width){
34891 this.currentTabWidth = width;
34892 for(var i = 0, len = this.items.length; i < len; i++) {
34893 if(!this.items[i].isHidden()) {
34894 this.items[i].setWidth(width);
34900 * Destroys this TabPanel
34901 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34903 destroy : function(removeEl){
34904 Roo.EventManager.removeResizeListener(this.onResize, this);
34905 for(var i = 0, len = this.items.length; i < len; i++){
34906 this.items[i].purgeListeners();
34908 if(removeEl === true){
34909 this.el.update("");
34914 createStrip : function(container)
34916 var strip = document.createElement("nav");
34917 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34918 container.appendChild(strip);
34922 createStripList : function(strip)
34924 // div wrapper for retard IE
34925 // returns the "tr" element.
34926 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34927 //'<div class="x-tabs-strip-wrap">'+
34928 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34929 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34930 return strip.firstChild; //.firstChild.firstChild.firstChild;
34932 createBody : function(container)
34934 var body = document.createElement("div");
34935 Roo.id(body, "tab-body");
34936 //Roo.fly(body).addClass("x-tabs-body");
34937 Roo.fly(body).addClass("tab-content");
34938 container.appendChild(body);
34941 createItemBody :function(bodyEl, id){
34942 var body = Roo.getDom(id);
34944 body = document.createElement("div");
34947 //Roo.fly(body).addClass("x-tabs-item-body");
34948 Roo.fly(body).addClass("tab-pane");
34949 bodyEl.insertBefore(body, bodyEl.firstChild);
34953 createStripElements : function(stripEl, text, closable)
34955 var td = document.createElement("li"); // was td..
34956 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34957 //stripEl.appendChild(td);
34959 td.className = "x-tabs-closable";
34960 if(!this.closeTpl){
34961 this.closeTpl = new Roo.Template(
34962 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34963 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34964 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
34967 var el = this.closeTpl.overwrite(td, {"text": text});
34968 var close = el.getElementsByTagName("div")[0];
34969 var inner = el.getElementsByTagName("em")[0];
34970 return {"el": el, "close": close, "inner": inner};
34973 // not sure what this is..
34975 //this.tabTpl = new Roo.Template(
34976 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34977 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34979 this.tabTpl = new Roo.Template(
34981 '<span unselectable="on"' +
34982 (this.disableTooltips ? '' : ' title="{text}"') +
34983 ' >{text}</span></span></a>'
34987 var el = this.tabTpl.overwrite(td, {"text": text});
34988 var inner = el.getElementsByTagName("span")[0];
34989 return {"el": el, "inner": inner};
34997 * @class Roo.TabPanelItem
34998 * @extends Roo.util.Observable
34999 * Represents an individual item (tab plus body) in a TabPanel.
35000 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35001 * @param {String} id The id of this TabPanelItem
35002 * @param {String} text The text for the tab of this TabPanelItem
35003 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35005 Roo.bootstrap.panel.TabItem = function(config){
35007 * The {@link Roo.TabPanel} this TabPanelItem belongs to
35008 * @type Roo.TabPanel
35010 this.tabPanel = config.panel;
35012 * The id for this TabPanelItem
35015 this.id = config.id;
35017 this.disabled = false;
35019 this.text = config.text;
35021 this.loaded = false;
35022 this.closable = config.closable;
35025 * The body element for this TabPanelItem.
35026 * @type Roo.Element
35028 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35029 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35030 this.bodyEl.setStyle("display", "block");
35031 this.bodyEl.setStyle("zoom", "1");
35032 //this.hideAction();
35034 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35036 this.el = Roo.get(els.el);
35037 this.inner = Roo.get(els.inner, true);
35038 this.textEl = Roo.get(this.el.dom.firstChild, true);
35039 this.pnode = Roo.get(els.el.parentNode, true);
35040 this.el.on("mousedown", this.onTabMouseDown, this);
35041 this.el.on("click", this.onTabClick, this);
35043 if(config.closable){
35044 var c = Roo.get(els.close, true);
35045 c.dom.title = this.closeText;
35046 c.addClassOnOver("close-over");
35047 c.on("click", this.closeClick, this);
35053 * Fires when this tab becomes the active tab.
35054 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35055 * @param {Roo.TabPanelItem} this
35059 * @event beforeclose
35060 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35061 * @param {Roo.TabPanelItem} this
35062 * @param {Object} e Set cancel to true on this object to cancel the close.
35064 "beforeclose": true,
35067 * Fires when this tab is closed.
35068 * @param {Roo.TabPanelItem} this
35072 * @event deactivate
35073 * Fires when this tab is no longer the active tab.
35074 * @param {Roo.TabPanel} tabPanel The parent TabPanel
35075 * @param {Roo.TabPanelItem} this
35077 "deactivate" : true
35079 this.hidden = false;
35081 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35084 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35086 purgeListeners : function(){
35087 Roo.util.Observable.prototype.purgeListeners.call(this);
35088 this.el.removeAllListeners();
35091 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35094 this.pnode.addClass("active");
35097 this.tabPanel.stripWrap.repaint();
35099 this.fireEvent("activate", this.tabPanel, this);
35103 * Returns true if this tab is the active tab.
35104 * @return {Boolean}
35106 isActive : function(){
35107 return this.tabPanel.getActiveTab() == this;
35111 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35114 this.pnode.removeClass("active");
35116 this.fireEvent("deactivate", this.tabPanel, this);
35119 hideAction : function(){
35120 this.bodyEl.hide();
35121 this.bodyEl.setStyle("position", "absolute");
35122 this.bodyEl.setLeft("-20000px");
35123 this.bodyEl.setTop("-20000px");
35126 showAction : function(){
35127 this.bodyEl.setStyle("position", "relative");
35128 this.bodyEl.setTop("");
35129 this.bodyEl.setLeft("");
35130 this.bodyEl.show();
35134 * Set the tooltip for the tab.
35135 * @param {String} tooltip The tab's tooltip
35137 setTooltip : function(text){
35138 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35139 this.textEl.dom.qtip = text;
35140 this.textEl.dom.removeAttribute('title');
35142 this.textEl.dom.title = text;
35146 onTabClick : function(e){
35147 e.preventDefault();
35148 this.tabPanel.activate(this.id);
35151 onTabMouseDown : function(e){
35152 e.preventDefault();
35153 this.tabPanel.activate(this.id);
35156 getWidth : function(){
35157 return this.inner.getWidth();
35160 setWidth : function(width){
35161 var iwidth = width - this.pnode.getPadding("lr");
35162 this.inner.setWidth(iwidth);
35163 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35164 this.pnode.setWidth(width);
35168 * Show or hide the tab
35169 * @param {Boolean} hidden True to hide or false to show.
35171 setHidden : function(hidden){
35172 this.hidden = hidden;
35173 this.pnode.setStyle("display", hidden ? "none" : "");
35177 * Returns true if this tab is "hidden"
35178 * @return {Boolean}
35180 isHidden : function(){
35181 return this.hidden;
35185 * Returns the text for this tab
35188 getText : function(){
35192 autoSize : function(){
35193 //this.el.beginMeasure();
35194 this.textEl.setWidth(1);
35196 * #2804 [new] Tabs in Roojs
35197 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35199 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35200 //this.el.endMeasure();
35204 * Sets the text for the tab (Note: this also sets the tooltip text)
35205 * @param {String} text The tab's text and tooltip
35207 setText : function(text){
35209 this.textEl.update(text);
35210 this.setTooltip(text);
35211 //if(!this.tabPanel.resizeTabs){
35212 // this.autoSize();
35216 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35218 activate : function(){
35219 this.tabPanel.activate(this.id);
35223 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35225 disable : function(){
35226 if(this.tabPanel.active != this){
35227 this.disabled = true;
35228 this.pnode.addClass("disabled");
35233 * Enables this TabPanelItem if it was previously disabled.
35235 enable : function(){
35236 this.disabled = false;
35237 this.pnode.removeClass("disabled");
35241 * Sets the content for this TabPanelItem.
35242 * @param {String} content The content
35243 * @param {Boolean} loadScripts true to look for and load scripts
35245 setContent : function(content, loadScripts){
35246 this.bodyEl.update(content, loadScripts);
35250 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35251 * @return {Roo.UpdateManager} The UpdateManager
35253 getUpdateManager : function(){
35254 return this.bodyEl.getUpdateManager();
35258 * Set a URL to be used to load the content for this TabPanelItem.
35259 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35260 * @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)
35261 * @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)
35262 * @return {Roo.UpdateManager} The UpdateManager
35264 setUrl : function(url, params, loadOnce){
35265 if(this.refreshDelegate){
35266 this.un('activate', this.refreshDelegate);
35268 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35269 this.on("activate", this.refreshDelegate);
35270 return this.bodyEl.getUpdateManager();
35274 _handleRefresh : function(url, params, loadOnce){
35275 if(!loadOnce || !this.loaded){
35276 var updater = this.bodyEl.getUpdateManager();
35277 updater.update(url, params, this._setLoaded.createDelegate(this));
35282 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
35283 * Will fail silently if the setUrl method has not been called.
35284 * This does not activate the panel, just updates its content.
35286 refresh : function(){
35287 if(this.refreshDelegate){
35288 this.loaded = false;
35289 this.refreshDelegate();
35294 _setLoaded : function(){
35295 this.loaded = true;
35299 closeClick : function(e){
35302 this.fireEvent("beforeclose", this, o);
35303 if(o.cancel !== true){
35304 this.tabPanel.removeTab(this.id);
35308 * The text displayed in the tooltip for the close icon.
35311 closeText : "Close this tab"