4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
107 // fill in the extra attributes
108 if (this.xattr && typeof(this.xattr) =='object') {
109 for (var i in this.xattr) {
110 cfg[i] = this.xattr[i];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
193 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195 return this.addxtypeChild(tree,cntr, is_body);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
234 addxtypeChild : function (tree, cntr, is_body)
236 Roo.debug && Roo.log('addxtypeChild:' + cntr);
238 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
241 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242 (typeof(tree['flexy:foreach']) != 'undefined');
246 skip_children = false;
247 // render the element if it's not BODY.
250 cn = Roo.factory(tree);
252 cn.parentType = this.xtype; //??
253 cn.parentId = this.id;
255 var build_from_html = Roo.XComponent.build_from_html;
258 // does the container contain child eleemnts with 'xtype' attributes.
259 // that match this xtype..
260 // note - when we render we create these as well..
261 // so we should check to see if body has xtype set.
262 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
264 var self_cntr_el = Roo.get(this[cntr](false));
265 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
267 //Roo.log(Roo.XComponent.build_from_html);
268 //Roo.log("got echild:");
271 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272 // and are not displayed -this causes this to use up the wrong element when matching.
273 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
276 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
283 //echild.dom.removeAttribute('xtype');
285 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286 Roo.debug && Roo.log(self_cntr_el);
287 Roo.debug && Roo.log(echild);
288 Roo.debug && Roo.log(cn);
294 // if object has flexy:if - then it may or may not be rendered.
295 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
296 // skip a flexy if element.
297 Roo.debug && Roo.log('skipping render');
298 Roo.debug && Roo.log(tree);
300 Roo.debug && Roo.log('skipping all children');
301 skip_children = true;
306 // actually if flexy:foreach is found, we really want to create
307 // multiple copies here...
309 //Roo.log(this[cntr]());
310 // some elements do not have render methods.. like the layouts...
311 cn.render && cn.render(this[cntr](true));
313 // then add the element..
321 if (typeof (tree.menu) != 'undefined') {
322 tree.menu.parentType = cn.xtype;
323 tree.menu.triggerEl = cn.el;
324 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
328 if (!tree.items || !tree.items.length) {
330 //Roo.log(["no children", this]);
335 var items = tree.items;
338 //Roo.log(items.length);
340 if (!skip_children) {
341 for(var i =0;i < items.length;i++) {
342 // Roo.log(['add child', items[i]]);
343 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
349 //Roo.log("fire childrenrendered");
351 cn.fireEvent('childrenrendered', this);
356 * Show a component - removes 'hidden' class
361 this.el.removeClass('hidden');
365 * Hide a component - adds 'hidden' class
369 if (this.el && !this.el.hasClass('hidden')) {
370 this.el.addClass('hidden');
384 * @class Roo.bootstrap.Body
385 * @extends Roo.bootstrap.Component
386 * Bootstrap Body class
390 * @param {Object} config The config object
393 Roo.bootstrap.Body = function(config){
394 Roo.bootstrap.Body.superclass.constructor.call(this, config);
395 this.el = Roo.get(document.body);
396 if (this.cls && this.cls.length) {
397 Roo.get(document.body).addClass(this.cls);
401 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
403 is_body : true,// just to make sure it's constructed?
408 onRender : function(ct, position)
410 /* Roo.log("Roo.bootstrap.Body - onRender");
411 if (this.cls && this.cls.length) {
412 Roo.get(document.body).addClass(this.cls);
432 * @class Roo.bootstrap.ButtonGroup
433 * @extends Roo.bootstrap.Component
434 * Bootstrap ButtonGroup class
435 * @cfg {String} size lg | sm | xs (default empty normal)
436 * @cfg {String} align vertical | justified (default none)
437 * @cfg {String} direction up | down (default down)
438 * @cfg {Boolean} toolbar false | true
439 * @cfg {Boolean} btn true | false
444 * @param {Object} config The config object
447 Roo.bootstrap.ButtonGroup = function(config){
448 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
459 getAutoCreate : function(){
465 cfg.html = this.html || cfg.html;
476 if (['vertical','justified'].indexOf(this.align)!==-1) {
477 cfg.cls = 'btn-group-' + this.align;
479 if (this.align == 'justified') {
480 console.log(this.items);
484 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
485 cfg.cls += ' btn-group-' + this.size;
488 if (this.direction == 'up') {
489 cfg.cls += ' dropup' ;
505 * @class Roo.bootstrap.Button
506 * @extends Roo.bootstrap.Component
507 * Bootstrap Button class
508 * @cfg {String} html The button content
509 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
510 * @cfg {String} size ( lg | sm | xs)
511 * @cfg {String} tag ( a | input | submit)
512 * @cfg {String} href empty or href
513 * @cfg {Boolean} disabled default false;
514 * @cfg {Boolean} isClose default false;
515 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
516 * @cfg {String} badge text for badge
517 * @cfg {String} theme default
518 * @cfg {Boolean} inverse
519 * @cfg {Boolean} toggle
520 * @cfg {String} ontext text for on toggle state
521 * @cfg {String} offtext text for off toggle state
522 * @cfg {Boolean} defaulton
523 * @cfg {Boolean} preventDefault default true
524 * @cfg {Boolean} removeClass remove the standard class..
525 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
528 * Create a new button
529 * @param {Object} config The config object
533 Roo.bootstrap.Button = function(config){
534 Roo.bootstrap.Button.superclass.constructor.call(this, config);
539 * When a butotn is pressed
540 * @param {Roo.bootstrap.Button} this
541 * @param {Roo.EventObject} e
546 * After the button has been toggles
547 * @param {Roo.EventObject} e
548 * @param {boolean} pressed (also available as button.pressed)
554 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
572 preventDefault: true,
581 getAutoCreate : function(){
589 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
590 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
595 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
597 if (this.toggle == true) {
600 cls: 'slider-frame roo-button',
605 'data-off-text':'OFF',
606 cls: 'slider-button',
612 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
613 cfg.cls += ' '+this.weight;
622 cfg["aria-hidden"] = true;
624 cfg.html = "×";
630 if (this.theme==='default') {
631 cfg.cls = 'btn roo-button';
633 //if (this.parentType != 'Navbar') {
634 this.weight = this.weight.length ? this.weight : 'default';
636 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
638 cfg.cls += ' btn-' + this.weight;
640 } else if (this.theme==='glow') {
643 cfg.cls = 'btn-glow roo-button';
645 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
647 cfg.cls += ' ' + this.weight;
653 this.cls += ' inverse';
658 cfg.cls += ' active';
662 cfg.disabled = 'disabled';
666 Roo.log('changing to ul' );
668 this.glyphicon = 'caret';
671 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
673 //gsRoo.log(this.parentType);
674 if (this.parentType === 'Navbar' && !this.parent().bar) {
675 Roo.log('changing to li?');
684 href : this.href || '#'
687 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
688 cfg.cls += ' dropdown';
695 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
697 if (this.glyphicon) {
698 cfg.html = ' ' + cfg.html;
703 cls: 'glyphicon glyphicon-' + this.glyphicon
713 // cfg.cls='btn roo-button';
717 var value = cfg.html;
722 cls: 'glyphicon glyphicon-' + this.glyphicon,
741 cfg.cls += ' dropdown';
742 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745 if (cfg.tag !== 'a' && this.href !== '') {
746 throw "Tag must be a to set href.";
747 } else if (this.href.length > 0) {
748 cfg.href = this.href;
751 if(this.removeClass){
756 cfg.target = this.target;
761 initEvents: function() {
762 // Roo.log('init events?');
763 // Roo.log(this.el.dom);
766 if (typeof (this.menu) != 'undefined') {
767 this.menu.parentType = this.xtype;
768 this.menu.triggerEl = this.el;
769 this.addxtype(Roo.apply({}, this.menu));
773 if (this.el.hasClass('roo-button')) {
774 this.el.on('click', this.onClick, this);
776 this.el.select('.roo-button').on('click', this.onClick, this);
779 if(this.removeClass){
780 this.el.on('click', this.onClick, this);
783 this.el.enableDisplayMode();
786 onClick : function(e)
793 Roo.log('button on click ');
794 if(this.preventDefault){
797 if (this.pressed === true || this.pressed === false) {
798 this.pressed = !this.pressed;
799 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
800 this.fireEvent('toggle', this, e, this.pressed);
804 this.fireEvent('click', this, e);
808 * Enables this button
812 this.disabled = false;
813 this.el.removeClass('disabled');
817 * Disable this button
821 this.disabled = true;
822 this.el.addClass('disabled');
825 * sets the active state on/off,
826 * @param {Boolean} state (optional) Force a particular state
828 setActive : function(v) {
830 this.el[v ? 'addClass' : 'removeClass']('active');
833 * toggles the current active state
835 toggleActive : function()
837 var active = this.el.hasClass('active');
838 this.setActive(!active);
842 setText : function(str)
844 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
848 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
871 * @class Roo.bootstrap.Column
872 * @extends Roo.bootstrap.Component
873 * Bootstrap Column class
874 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
875 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
876 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
877 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
878 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
879 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
880 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
881 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884 * @cfg {Boolean} hidden (true|false) hide the element
885 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
886 * @cfg {String} fa (ban|check|...) font awesome icon
887 * @cfg {Number} fasize (1|2|....) font awsome size
889 * @cfg {String} icon (info-sign|check|...) glyphicon name
891 * @cfg {String} html content of column.
894 * Create a new Column
895 * @param {Object} config The config object
898 Roo.bootstrap.Column = function(config){
899 Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
920 getAutoCreate : function(){
921 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
929 ['xs','sm','md','lg'].map(function(size){
930 //Roo.log( size + ':' + settings[size]);
932 if (settings[size+'off'] !== false) {
933 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936 if (settings[size] === false) {
940 if (!settings[size]) { // 0 = hidden
941 cfg.cls += ' hidden-' + size;
944 cfg.cls += ' col-' + size + '-' + settings[size];
949 cfg.cls += ' hidden';
952 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
953 cfg.cls +=' alert alert-' + this.alert;
957 if (this.html.length) {
958 cfg.html = this.html;
962 if (this.fasize > 1) {
963 fasize = ' fa-' + this.fasize + 'x';
965 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
970 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
989 * @class Roo.bootstrap.Container
990 * @extends Roo.bootstrap.Component
991 * Bootstrap Container class
992 * @cfg {Boolean} jumbotron is it a jumbotron element
993 * @cfg {String} html content of element
994 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
995 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
996 * @cfg {String} header content of header (for panel)
997 * @cfg {String} footer content of footer (for panel)
998 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
999 * @cfg {String} tag (header|aside|section) type of HTML tag.
1000 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1001 * @cfg {String} fa font awesome icon
1002 * @cfg {String} icon (info-sign|check|...) glyphicon name
1003 * @cfg {Boolean} hidden (true|false) hide the element
1004 * @cfg {Boolean} expandable (true|false) default false
1005 * @cfg {Boolean} expanded (true|false) default true
1006 * @cfg {String} rheader contet on the right of header
1007 * @cfg {Boolean} clickable (true|false) default false
1011 * Create a new Container
1012 * @param {Object} config The config object
1015 Roo.bootstrap.Container = function(config){
1016 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1022 * After the panel has been expand
1024 * @param {Roo.bootstrap.Container} this
1029 * After the panel has been collapsed
1031 * @param {Roo.bootstrap.Container} this
1036 * When a element is chick
1037 * @param {Roo.bootstrap.Container} this
1038 * @param {Roo.EventObject} e
1044 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1062 getChildContainer : function() {
1068 if (this.panel.length) {
1069 return this.el.select('.panel-body',true).first();
1076 getAutoCreate : function(){
1079 tag : this.tag || 'div',
1083 if (this.jumbotron) {
1084 cfg.cls = 'jumbotron';
1089 // - this is applied by the parent..
1091 // cfg.cls = this.cls + '';
1094 if (this.sticky.length) {
1096 var bd = Roo.get(document.body);
1097 if (!bd.hasClass('bootstrap-sticky')) {
1098 bd.addClass('bootstrap-sticky');
1099 Roo.select('html',true).setStyle('height', '100%');
1102 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1106 if (this.well.length) {
1107 switch (this.well) {
1110 cfg.cls +=' well well-' +this.well;
1119 cfg.cls += ' hidden';
1123 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1124 cfg.cls +=' alert alert-' + this.alert;
1129 if (this.panel.length) {
1130 cfg.cls += ' panel panel-' + this.panel;
1132 if (this.header.length) {
1136 if(this.expandable){
1138 cfg.cls = cfg.cls + ' expandable';
1142 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1150 cls : 'panel-title',
1151 html : (this.expandable ? ' ' : '') + this.header
1155 cls: 'panel-header-right',
1161 cls : 'panel-heading',
1162 style : this.expandable ? 'cursor: pointer' : '',
1170 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1175 if (this.footer.length) {
1177 cls : 'panel-footer',
1186 body.html = this.html || cfg.html;
1187 // prefix with the icons..
1189 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1197 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1198 cfg.cls = 'container';
1204 initEvents: function()
1206 if(this.expandable){
1207 var headerEl = this.headerEl();
1210 headerEl.on('click', this.onToggleClick, this);
1215 this.el.on('click', this.onClick, this);
1220 onToggleClick : function()
1222 var headerEl = this.headerEl();
1238 if(this.fireEvent('expand', this)) {
1240 this.expanded = true;
1242 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1244 this.el.select('.panel-body',true).first().removeClass('hide');
1246 var toggleEl = this.toggleEl();
1252 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1257 collapse : function()
1259 if(this.fireEvent('collapse', this)) {
1261 this.expanded = false;
1263 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1264 this.el.select('.panel-body',true).first().addClass('hide');
1266 var toggleEl = this.toggleEl();
1272 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1276 toggleEl : function()
1278 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1282 return this.el.select('.panel-heading .fa',true).first();
1285 headerEl : function()
1287 if(!this.el || !this.panel.length || !this.header.length){
1291 return this.el.select('.panel-heading',true).first()
1294 titleEl : function()
1296 if(!this.el || !this.panel.length || !this.header.length){
1300 return this.el.select('.panel-title',true).first();
1303 setTitle : function(v)
1305 var titleEl = this.titleEl();
1311 titleEl.dom.innerHTML = v;
1314 getTitle : function()
1317 var titleEl = this.titleEl();
1323 return titleEl.dom.innerHTML;
1326 setRightTitle : function(v)
1328 var t = this.el.select('.panel-header-right',true).first();
1334 t.dom.innerHTML = v;
1337 onClick : function(e)
1341 this.fireEvent('click', this, e);
1355 * @class Roo.bootstrap.Img
1356 * @extends Roo.bootstrap.Component
1357 * Bootstrap Img class
1358 * @cfg {Boolean} imgResponsive false | true
1359 * @cfg {String} border rounded | circle | thumbnail
1360 * @cfg {String} src image source
1361 * @cfg {String} alt image alternative text
1362 * @cfg {String} href a tag href
1363 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1364 * @cfg {String} xsUrl xs image source
1365 * @cfg {String} smUrl sm image source
1366 * @cfg {String} mdUrl md image source
1367 * @cfg {String} lgUrl lg image source
1370 * Create a new Input
1371 * @param {Object} config The config object
1374 Roo.bootstrap.Img = function(config){
1375 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1381 * The img click event for the img.
1382 * @param {Roo.EventObject} e
1388 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1390 imgResponsive: true,
1400 getAutoCreate : function()
1402 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1403 return this.createSingleImg();
1408 cls: 'roo-image-responsive-group',
1413 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1415 if(!_this[size + 'Url']){
1421 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1422 html: _this.html || cfg.html,
1423 src: _this[size + 'Url']
1426 img.cls += ' roo-image-responsive-' + size;
1428 var s = ['xs', 'sm', 'md', 'lg'];
1430 s.splice(s.indexOf(size), 1);
1432 Roo.each(s, function(ss){
1433 img.cls += ' hidden-' + ss;
1436 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1437 cfg.cls += ' img-' + _this.border;
1441 cfg.alt = _this.alt;
1454 a.target = _this.target;
1458 cfg.cn.push((_this.href) ? a : img);
1465 createSingleImg : function()
1469 cls: (this.imgResponsive) ? 'img-responsive' : '',
1471 src : 'about:blank' // just incase src get's set to undefined?!?
1474 cfg.html = this.html || cfg.html;
1476 cfg.src = this.src || cfg.src;
1478 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1479 cfg.cls += ' img-' + this.border;
1496 a.target = this.target;
1501 return (this.href) ? a : cfg;
1504 initEvents: function()
1507 this.el.on('click', this.onClick, this);
1512 onClick : function(e)
1514 Roo.log('img onclick');
1515 this.fireEvent('click', this, e);
1529 * @class Roo.bootstrap.Link
1530 * @extends Roo.bootstrap.Component
1531 * Bootstrap Link Class
1532 * @cfg {String} alt image alternative text
1533 * @cfg {String} href a tag href
1534 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1535 * @cfg {String} html the content of the link.
1536 * @cfg {String} anchor name for the anchor link
1537 * @cfg {String} fa - favicon
1539 * @cfg {Boolean} preventDefault (true | false) default false
1543 * Create a new Input
1544 * @param {Object} config The config object
1547 Roo.bootstrap.Link = function(config){
1548 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1554 * The img click event for the img.
1555 * @param {Roo.EventObject} e
1561 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1565 preventDefault: false,
1571 getAutoCreate : function()
1573 var html = this.html || '';
1575 if (this.fa !== false) {
1576 html = '<i class="fa fa-' + this.fa + '"></i>';
1581 // anchor's do not require html/href...
1582 if (this.anchor === false) {
1584 cfg.href = this.href || '#';
1586 cfg.name = this.anchor;
1587 if (this.html !== false || this.fa !== false) {
1590 if (this.href !== false) {
1591 cfg.href = this.href;
1595 if(this.alt !== false){
1600 if(this.target !== false) {
1601 cfg.target = this.target;
1607 initEvents: function() {
1609 if(!this.href || this.preventDefault){
1610 this.el.on('click', this.onClick, this);
1614 onClick : function(e)
1616 if(this.preventDefault){
1619 //Roo.log('img onclick');
1620 this.fireEvent('click', this, e);
1633 * @class Roo.bootstrap.Header
1634 * @extends Roo.bootstrap.Component
1635 * Bootstrap Header class
1636 * @cfg {String} html content of header
1637 * @cfg {Number} level (1|2|3|4|5|6) default 1
1640 * Create a new Header
1641 * @param {Object} config The config object
1645 Roo.bootstrap.Header = function(config){
1646 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1649 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1657 getAutoCreate : function(){
1662 tag: 'h' + (1 *this.level),
1663 html: this.html || ''
1675 * Ext JS Library 1.1.1
1676 * Copyright(c) 2006-2007, Ext JS, LLC.
1678 * Originally Released Under LGPL - original licence link has changed is not relivant.
1681 * <script type="text/javascript">
1685 * @class Roo.bootstrap.MenuMgr
1686 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1689 Roo.bootstrap.MenuMgr = function(){
1690 var menus, active, groups = {}, attached = false, lastShow = new Date();
1692 // private - called when first menu is created
1695 active = new Roo.util.MixedCollection();
1696 Roo.get(document).addKeyListener(27, function(){
1697 if(active.length > 0){
1705 if(active && active.length > 0){
1706 var c = active.clone();
1716 if(active.length < 1){
1717 Roo.get(document).un("mouseup", onMouseDown);
1725 var last = active.last();
1726 lastShow = new Date();
1729 Roo.get(document).on("mouseup", onMouseDown);
1734 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1735 m.parentMenu.activeChild = m;
1736 }else if(last && last.isVisible()){
1737 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1742 function onBeforeHide(m){
1744 m.activeChild.hide();
1746 if(m.autoHideTimer){
1747 clearTimeout(m.autoHideTimer);
1748 delete m.autoHideTimer;
1753 function onBeforeShow(m){
1754 var pm = m.parentMenu;
1755 if(!pm && !m.allowOtherMenus){
1757 }else if(pm && pm.activeChild && active != m){
1758 pm.activeChild.hide();
1762 // private this should really trigger on mouseup..
1763 function onMouseDown(e){
1764 Roo.log("on Mouse Up");
1766 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1767 Roo.log("MenuManager hideAll");
1776 function onBeforeCheck(mi, state){
1778 var g = groups[mi.group];
1779 for(var i = 0, l = g.length; i < l; i++){
1781 g[i].setChecked(false);
1790 * Hides all menus that are currently visible
1792 hideAll : function(){
1797 register : function(menu){
1801 menus[menu.id] = menu;
1802 menu.on("beforehide", onBeforeHide);
1803 menu.on("hide", onHide);
1804 menu.on("beforeshow", onBeforeShow);
1805 menu.on("show", onShow);
1807 if(g && menu.events["checkchange"]){
1811 groups[g].push(menu);
1812 menu.on("checkchange", onCheck);
1817 * Returns a {@link Roo.menu.Menu} object
1818 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1819 * be used to generate and return a new Menu instance.
1821 get : function(menu){
1822 if(typeof menu == "string"){ // menu id
1824 }else if(menu.events){ // menu instance
1827 /*else if(typeof menu.length == 'number'){ // array of menu items?
1828 return new Roo.bootstrap.Menu({items:menu});
1829 }else{ // otherwise, must be a config
1830 return new Roo.bootstrap.Menu(menu);
1837 unregister : function(menu){
1838 delete menus[menu.id];
1839 menu.un("beforehide", onBeforeHide);
1840 menu.un("hide", onHide);
1841 menu.un("beforeshow", onBeforeShow);
1842 menu.un("show", onShow);
1844 if(g && menu.events["checkchange"]){
1845 groups[g].remove(menu);
1846 menu.un("checkchange", onCheck);
1851 registerCheckable : function(menuItem){
1852 var g = menuItem.group;
1857 groups[g].push(menuItem);
1858 menuItem.on("beforecheckchange", onBeforeCheck);
1863 unregisterCheckable : function(menuItem){
1864 var g = menuItem.group;
1866 groups[g].remove(menuItem);
1867 menuItem.un("beforecheckchange", onBeforeCheck);
1879 * @class Roo.bootstrap.Menu
1880 * @extends Roo.bootstrap.Component
1881 * Bootstrap Menu class - container for MenuItems
1882 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1883 * @cfg {bool} hidden if the menu should be hidden when rendered.
1884 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1885 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1889 * @param {Object} config The config object
1893 Roo.bootstrap.Menu = function(config){
1894 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1895 if (this.registerMenu && this.type != 'treeview') {
1896 Roo.bootstrap.MenuMgr.register(this);
1901 * Fires before this menu is displayed
1902 * @param {Roo.menu.Menu} this
1907 * Fires before this menu is hidden
1908 * @param {Roo.menu.Menu} this
1913 * Fires after this menu is displayed
1914 * @param {Roo.menu.Menu} this
1919 * Fires after this menu is hidden
1920 * @param {Roo.menu.Menu} this
1925 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1926 * @param {Roo.menu.Menu} this
1927 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1928 * @param {Roo.EventObject} e
1933 * Fires when the mouse is hovering over this menu
1934 * @param {Roo.menu.Menu} this
1935 * @param {Roo.EventObject} e
1936 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1941 * Fires when the mouse exits this menu
1942 * @param {Roo.menu.Menu} this
1943 * @param {Roo.EventObject} e
1944 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1949 * Fires when a menu item contained in this menu is clicked
1950 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1951 * @param {Roo.EventObject} e
1955 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1958 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1962 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1965 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1967 registerMenu : true,
1969 menuItems :false, // stores the menu items..
1979 getChildContainer : function() {
1983 getAutoCreate : function(){
1985 //if (['right'].indexOf(this.align)!==-1) {
1986 // cfg.cn[1].cls += ' pull-right'
1992 cls : 'dropdown-menu' ,
1993 style : 'z-index:1000'
1997 if (this.type === 'submenu') {
1998 cfg.cls = 'submenu active';
2000 if (this.type === 'treeview') {
2001 cfg.cls = 'treeview-menu';
2006 initEvents : function() {
2008 // Roo.log("ADD event");
2009 // Roo.log(this.triggerEl.dom);
2011 this.triggerEl.on('click', this.onTriggerClick, this);
2013 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2015 this.triggerEl.addClass('dropdown-toggle');
2018 this.el.on('touchstart' , this.onTouch, this);
2020 this.el.on('click' , this.onClick, this);
2022 this.el.on("mouseover", this.onMouseOver, this);
2023 this.el.on("mouseout", this.onMouseOut, this);
2027 findTargetItem : function(e)
2029 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2033 //Roo.log(t); Roo.log(t.id);
2035 //Roo.log(this.menuitems);
2036 return this.menuitems.get(t.id);
2038 //return this.items.get(t.menuItemId);
2044 onTouch : function(e)
2046 Roo.log("menu.onTouch");
2047 //e.stopEvent(); this make the user popdown broken
2051 onClick : function(e)
2053 Roo.log("menu.onClick");
2055 var t = this.findTargetItem(e);
2056 if(!t || t.isContainer){
2061 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2062 if(t == this.activeItem && t.shouldDeactivate(e)){
2063 this.activeItem.deactivate();
2064 delete this.activeItem;
2068 this.setActiveItem(t, true);
2076 Roo.log('pass click event');
2080 this.fireEvent("click", this, t, e);
2084 (function() { _this.hide(); }).defer(500);
2087 onMouseOver : function(e){
2088 var t = this.findTargetItem(e);
2091 // if(t.canActivate && !t.disabled){
2092 // this.setActiveItem(t, true);
2096 this.fireEvent("mouseover", this, e, t);
2098 isVisible : function(){
2099 return !this.hidden;
2101 onMouseOut : function(e){
2102 var t = this.findTargetItem(e);
2105 // if(t == this.activeItem && t.shouldDeactivate(e)){
2106 // this.activeItem.deactivate();
2107 // delete this.activeItem;
2110 this.fireEvent("mouseout", this, e, t);
2115 * Displays this menu relative to another element
2116 * @param {String/HTMLElement/Roo.Element} element The element to align to
2117 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2118 * the element (defaults to this.defaultAlign)
2119 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2121 show : function(el, pos, parentMenu){
2122 this.parentMenu = parentMenu;
2126 this.fireEvent("beforeshow", this);
2127 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2130 * Displays this menu at a specific xy position
2131 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2132 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2134 showAt : function(xy, parentMenu, /* private: */_e){
2135 this.parentMenu = parentMenu;
2140 this.fireEvent("beforeshow", this);
2141 //xy = this.el.adjustForConstraints(xy);
2145 this.hideMenuItems();
2146 this.hidden = false;
2147 this.triggerEl.addClass('open');
2149 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2150 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2153 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2158 this.fireEvent("show", this);
2164 this.doFocus.defer(50, this);
2168 doFocus : function(){
2170 this.focusEl.focus();
2175 * Hides this menu and optionally all parent menus
2176 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2178 hide : function(deep)
2181 this.hideMenuItems();
2182 if(this.el && this.isVisible()){
2183 this.fireEvent("beforehide", this);
2184 if(this.activeItem){
2185 this.activeItem.deactivate();
2186 this.activeItem = null;
2188 this.triggerEl.removeClass('open');;
2190 this.fireEvent("hide", this);
2192 if(deep === true && this.parentMenu){
2193 this.parentMenu.hide(true);
2197 onTriggerClick : function(e)
2199 Roo.log('trigger click');
2201 var target = e.getTarget();
2203 Roo.log(target.nodeName.toLowerCase());
2205 if(target.nodeName.toLowerCase() === 'i'){
2211 onTriggerPress : function(e)
2213 Roo.log('trigger press');
2214 //Roo.log(e.getTarget());
2215 // Roo.log(this.triggerEl.dom);
2217 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2218 var pel = Roo.get(e.getTarget());
2219 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2220 Roo.log('is treeview or dropdown?');
2224 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2228 if (this.isVisible()) {
2233 this.show(this.triggerEl, false, false);
2236 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2243 hideMenuItems : function()
2245 Roo.log("hide Menu Items");
2249 //$(backdrop).remove()
2250 this.el.select('.open',true).each(function(aa) {
2252 aa.removeClass('open');
2253 //var parent = getParent($(this))
2254 //var relatedTarget = { relatedTarget: this }
2256 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2257 //if (e.isDefaultPrevented()) return
2258 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2261 addxtypeChild : function (tree, cntr) {
2262 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2264 this.menuitems.add(comp);
2285 * @class Roo.bootstrap.MenuItem
2286 * @extends Roo.bootstrap.Component
2287 * Bootstrap MenuItem class
2288 * @cfg {String} html the menu label
2289 * @cfg {String} href the link
2290 * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2291 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2292 * @cfg {Boolean} active used on sidebars to highlight active itesm
2293 * @cfg {String} fa favicon to show on left of menu item.
2294 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2298 * Create a new MenuItem
2299 * @param {Object} config The config object
2303 Roo.bootstrap.MenuItem = function(config){
2304 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2309 * The raw click event for the entire grid.
2310 * @param {Roo.bootstrap.MenuItem} this
2311 * @param {Roo.EventObject} e
2317 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2321 preventDefault: true,
2322 isContainer : false,
2326 getAutoCreate : function(){
2328 if(this.isContainer){
2331 cls: 'dropdown-menu-item'
2345 if (this.fa !== false) {
2348 cls : 'fa fa-' + this.fa
2357 cls: 'dropdown-menu-item',
2360 if (this.parent().type == 'treeview') {
2361 cfg.cls = 'treeview-menu';
2364 cfg.cls += ' active';
2369 anc.href = this.href || cfg.cn[0].href ;
2370 ctag.html = this.html || cfg.cn[0].html ;
2374 initEvents: function()
2376 if (this.parent().type == 'treeview') {
2377 this.el.select('a').on('click', this.onClick, this);
2380 this.menu.parentType = this.xtype;
2381 this.menu.triggerEl = this.el;
2382 this.menu = this.addxtype(Roo.apply({}, this.menu));
2386 onClick : function(e)
2388 Roo.log('item on click ');
2389 //if(this.preventDefault){
2390 // e.preventDefault();
2392 //this.parent().hideMenuItems();
2394 this.fireEvent('click', this, e);
2413 * @class Roo.bootstrap.MenuSeparator
2414 * @extends Roo.bootstrap.Component
2415 * Bootstrap MenuSeparator class
2418 * Create a new MenuItem
2419 * @param {Object} config The config object
2423 Roo.bootstrap.MenuSeparator = function(config){
2424 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2427 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2429 getAutoCreate : function(){
2448 * @class Roo.bootstrap.Modal
2449 * @extends Roo.bootstrap.Component
2450 * Bootstrap Modal class
2451 * @cfg {String} title Title of dialog
2452 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2453 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2454 * @cfg {Boolean} specificTitle default false
2455 * @cfg {Array} buttons Array of buttons or standard button set..
2456 * @cfg {String} buttonPosition (left|right|center) default right
2457 * @cfg {Boolean} animate default true
2458 * @cfg {Boolean} allow_close default true
2459 * @cfg {Boolean} fitwindow default true
2460 * @cfg {String} size (sm|lg) default empty
2464 * Create a new Modal Dialog
2465 * @param {Object} config The config object
2468 Roo.bootstrap.Modal = function(config){
2469 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2474 * The raw btnclick event for the button
2475 * @param {Roo.EventObject} e
2479 this.buttons = this.buttons || [];
2482 this.tmpl = Roo.factory(this.tmpl);
2487 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2489 title : 'test dialog',
2499 specificTitle: false,
2501 buttonPosition: 'right',
2520 onRender : function(ct, position)
2522 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2525 var cfg = Roo.apply({}, this.getAutoCreate());
2528 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2530 //if (!cfg.name.length) {
2534 cfg.cls += ' ' + this.cls;
2537 cfg.style = this.style;
2539 this.el = Roo.get(document.body).createChild(cfg, position);
2541 //var type = this.el.dom.type;
2544 if(this.tabIndex !== undefined){
2545 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2548 this.dialogEl = this.el.select('.modal-dialog',true).first();
2549 this.bodyEl = this.el.select('.modal-body',true).first();
2550 this.closeEl = this.el.select('.modal-header .close', true).first();
2551 this.footerEl = this.el.select('.modal-footer',true).first();
2552 this.titleEl = this.el.select('.modal-title',true).first();
2556 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2557 this.maskEl.enableDisplayMode("block");
2559 //this.el.addClass("x-dlg-modal");
2561 if (this.buttons.length) {
2562 Roo.each(this.buttons, function(bb) {
2563 var b = Roo.apply({}, bb);
2564 b.xns = b.xns || Roo.bootstrap;
2565 b.xtype = b.xtype || 'Button';
2566 if (typeof(b.listeners) == 'undefined') {
2567 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2570 var btn = Roo.factory(b);
2572 btn.render(this.el.select('.modal-footer div').first());
2576 // render the children.
2579 if(typeof(this.items) != 'undefined'){
2580 var items = this.items;
2583 for(var i =0;i < items.length;i++) {
2584 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2588 this.items = nitems;
2590 // where are these used - they used to be body/close/footer
2594 //this.el.addClass([this.fieldClass, this.cls]);
2598 getAutoCreate : function(){
2603 html : this.html || ''
2608 cls : 'modal-title',
2612 if(this.specificTitle){
2618 if (this.allow_close) {
2630 if(this.size.length){
2631 size = 'modal-' + this.size;
2636 style : 'display: none',
2639 cls: "modal-dialog " + size,
2642 cls : "modal-content",
2645 cls : 'modal-header',
2650 cls : 'modal-footer',
2654 cls: 'btn-' + this.buttonPosition
2671 modal.cls += ' fade';
2677 getChildContainer : function() {
2682 getButtonContainer : function() {
2683 return this.el.select('.modal-footer div',true).first();
2686 initEvents : function()
2688 if (this.allow_close) {
2689 this.closeEl.on('click', this.hide, this);
2691 Roo.EventManager.onWindowResize(this.resize, this, true);
2698 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2699 if (this.fitwindow) {
2700 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2701 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 30;
2706 setSize : function(w,h)
2716 if (!this.rendered) {
2720 this.el.setStyle('display', 'block');
2722 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2725 this.el.addClass('in');
2728 this.el.addClass('in');
2732 // not sure how we can show data in here..
2734 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2737 Roo.get(document.body).addClass("x-body-masked");
2738 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2740 this.el.setStyle('zIndex', '10001');
2742 this.fireEvent('show', this);
2743 this.items.forEach(function(e) {
2744 e.layout ? e.layout() : false;
2755 Roo.get(document.body).removeClass("x-body-masked");
2756 this.el.removeClass('in');
2757 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2759 if(this.animate){ // why
2761 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2763 this.el.setStyle('display', 'none');
2766 this.fireEvent('hide', this);
2769 addButton : function(str, cb)
2773 var b = Roo.apply({}, { html : str } );
2774 b.xns = b.xns || Roo.bootstrap;
2775 b.xtype = b.xtype || 'Button';
2776 if (typeof(b.listeners) == 'undefined') {
2777 b.listeners = { click : cb.createDelegate(this) };
2780 var btn = Roo.factory(b);
2782 btn.render(this.el.select('.modal-footer div').first());
2788 setDefaultButton : function(btn)
2790 //this.el.select('.modal-footer').()
2794 resizeTo: function(w,h)
2798 this.dialogEl.setWidth(w);
2799 if (this.diff === false) {
2800 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2803 this.bodyEl.setHeight(h-this.diff);
2807 setContentSize : function(w, h)
2811 onButtonClick: function(btn,e)
2814 this.fireEvent('btnclick', btn.name, e);
2817 * Set the title of the Dialog
2818 * @param {String} str new Title
2820 setTitle: function(str) {
2821 this.titleEl.dom.innerHTML = str;
2824 * Set the body of the Dialog
2825 * @param {String} str new Title
2827 setBody: function(str) {
2828 this.bodyEl.dom.innerHTML = str;
2831 * Set the body of the Dialog using the template
2832 * @param {Obj} data - apply this data to the template and replace the body contents.
2834 applyBody: function(obj)
2837 Roo.log("Error - using apply Body without a template");
2840 this.tmpl.overwrite(this.bodyEl, obj);
2846 Roo.apply(Roo.bootstrap.Modal, {
2848 * Button config that displays a single OK button
2857 * Button config that displays Yes and No buttons
2873 * Button config that displays OK and Cancel buttons
2888 * Button config that displays Yes, No and Cancel buttons
2911 * messagebox - can be used as a replace
2915 * @class Roo.MessageBox
2916 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2920 Roo.Msg.alert('Status', 'Changes saved successfully.');
2922 // Prompt for user data:
2923 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2925 // process text value...
2929 // Show a dialog using config options:
2931 title:'Save Changes?',
2932 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2933 buttons: Roo.Msg.YESNOCANCEL,
2940 Roo.bootstrap.MessageBox = function(){
2941 var dlg, opt, mask, waitTimer;
2942 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2943 var buttons, activeTextEl, bwidth;
2947 var handleButton = function(button){
2949 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2953 var handleHide = function(){
2955 dlg.el.removeClass(opt.cls);
2958 // Roo.TaskMgr.stop(waitTimer);
2959 // waitTimer = null;
2964 var updateButtons = function(b){
2967 buttons["ok"].hide();
2968 buttons["cancel"].hide();
2969 buttons["yes"].hide();
2970 buttons["no"].hide();
2971 //dlg.footer.dom.style.display = 'none';
2974 dlg.footerEl.dom.style.display = '';
2975 for(var k in buttons){
2976 if(typeof buttons[k] != "function"){
2979 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2980 width += buttons[k].el.getWidth()+15;
2990 var handleEsc = function(d, k, e){
2991 if(opt && opt.closable !== false){
3001 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3002 * @return {Roo.BasicDialog} The BasicDialog element
3004 getDialog : function(){
3006 dlg = new Roo.bootstrap.Modal( {
3009 //constraintoviewport:false,
3011 //collapsible : false,
3016 //buttonAlign:"center",
3017 closeClick : function(){
3018 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3021 handleButton("cancel");
3026 dlg.on("hide", handleHide);
3028 //dlg.addKeyListener(27, handleEsc);
3030 this.buttons = buttons;
3031 var bt = this.buttonText;
3032 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3033 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3034 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3035 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3037 bodyEl = dlg.bodyEl.createChild({
3039 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3040 '<textarea class="roo-mb-textarea"></textarea>' +
3041 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3043 msgEl = bodyEl.dom.firstChild;
3044 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3045 textboxEl.enableDisplayMode();
3046 textboxEl.addKeyListener([10,13], function(){
3047 if(dlg.isVisible() && opt && opt.buttons){
3050 }else if(opt.buttons.yes){
3051 handleButton("yes");
3055 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3056 textareaEl.enableDisplayMode();
3057 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3058 progressEl.enableDisplayMode();
3059 var pf = progressEl.dom.firstChild;
3061 pp = Roo.get(pf.firstChild);
3062 pp.setHeight(pf.offsetHeight);
3070 * Updates the message box body text
3071 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3072 * the XHTML-compliant non-breaking space character '&#160;')
3073 * @return {Roo.MessageBox} This message box
3075 updateText : function(text){
3076 if(!dlg.isVisible() && !opt.width){
3077 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3079 msgEl.innerHTML = text || ' ';
3081 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3082 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3084 Math.min(opt.width || cw , this.maxWidth),
3085 Math.max(opt.minWidth || this.minWidth, bwidth)
3088 activeTextEl.setWidth(w);
3090 if(dlg.isVisible()){
3091 dlg.fixedcenter = false;
3093 // to big, make it scroll. = But as usual stupid IE does not support
3096 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3097 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3098 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3100 bodyEl.dom.style.height = '';
3101 bodyEl.dom.style.overflowY = '';
3104 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3106 bodyEl.dom.style.overflowX = '';
3109 dlg.setContentSize(w, bodyEl.getHeight());
3110 if(dlg.isVisible()){
3111 dlg.fixedcenter = true;
3117 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3118 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3119 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3120 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3121 * @return {Roo.MessageBox} This message box
3123 updateProgress : function(value, text){
3125 this.updateText(text);
3127 if (pp) { // weird bug on my firefox - for some reason this is not defined
3128 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3134 * Returns true if the message box is currently displayed
3135 * @return {Boolean} True if the message box is visible, else false
3137 isVisible : function(){
3138 return dlg && dlg.isVisible();
3142 * Hides the message box if it is displayed
3145 if(this.isVisible()){
3151 * Displays a new message box, or reinitializes an existing message box, based on the config options
3152 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3153 * The following config object properties are supported:
3155 Property Type Description
3156 ---------- --------------- ------------------------------------------------------------------------------------
3157 animEl String/Element An id or Element from which the message box should animate as it opens and
3158 closes (defaults to undefined)
3159 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3160 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3161 closable Boolean False to hide the top-right close button (defaults to true). Note that
3162 progress and wait dialogs will ignore this property and always hide the
3163 close button as they can only be closed programmatically.
3164 cls String A custom CSS class to apply to the message box element
3165 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3166 displayed (defaults to 75)
3167 fn Function A callback function to execute after closing the dialog. The arguments to the
3168 function will be btn (the name of the button that was clicked, if applicable,
3169 e.g. "ok"), and text (the value of the active text field, if applicable).
3170 Progress and wait dialogs will ignore this option since they do not respond to
3171 user actions and can only be closed programmatically, so any required function
3172 should be called by the same code after it closes the dialog.
3173 icon String A CSS class that provides a background image to be used as an icon for
3174 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3175 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3176 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3177 modal Boolean False to allow user interaction with the page while the message box is
3178 displayed (defaults to true)
3179 msg String A string that will replace the existing message box body text (defaults
3180 to the XHTML-compliant non-breaking space character ' ')
3181 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3182 progress Boolean True to display a progress bar (defaults to false)
3183 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3184 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3185 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3186 title String The title text
3187 value String The string value to set into the active textbox element if displayed
3188 wait Boolean True to display a progress bar (defaults to false)
3189 width Number The width of the dialog in pixels
3196 msg: 'Please enter your address:',
3198 buttons: Roo.MessageBox.OKCANCEL,
3201 animEl: 'addAddressBtn'
3204 * @param {Object} config Configuration options
3205 * @return {Roo.MessageBox} This message box
3207 show : function(options)
3210 // this causes nightmares if you show one dialog after another
3211 // especially on callbacks..
3213 if(this.isVisible()){
3216 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3217 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3218 Roo.log("New Dialog Message:" + options.msg )
3219 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3220 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3223 var d = this.getDialog();
3225 d.setTitle(opt.title || " ");
3226 d.closeEl.setDisplayed(opt.closable !== false);
3227 activeTextEl = textboxEl;
3228 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3233 textareaEl.setHeight(typeof opt.multiline == "number" ?
3234 opt.multiline : this.defaultTextHeight);
3235 activeTextEl = textareaEl;
3244 progressEl.setDisplayed(opt.progress === true);
3245 this.updateProgress(0);
3246 activeTextEl.dom.value = opt.value || "";
3248 dlg.setDefaultButton(activeTextEl);
3250 var bs = opt.buttons;
3254 }else if(bs && bs.yes){
3255 db = buttons["yes"];
3257 dlg.setDefaultButton(db);
3259 bwidth = updateButtons(opt.buttons);
3260 this.updateText(opt.msg);
3262 d.el.addClass(opt.cls);
3264 d.proxyDrag = opt.proxyDrag === true;
3265 d.modal = opt.modal !== false;
3266 d.mask = opt.modal !== false ? mask : false;
3268 // force it to the end of the z-index stack so it gets a cursor in FF
3269 document.body.appendChild(dlg.el.dom);
3270 d.animateTarget = null;
3271 d.show(options.animEl);
3277 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3278 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3279 * and closing the message box when the process is complete.
3280 * @param {String} title The title bar text
3281 * @param {String} msg The message box body text
3282 * @return {Roo.MessageBox} This message box
3284 progress : function(title, msg){
3291 minWidth: this.minProgressWidth,
3298 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3299 * If a callback function is passed it will be called after the user clicks the button, and the
3300 * id of the button that was clicked will be passed as the only parameter to the callback
3301 * (could also be the top-right close button).
3302 * @param {String} title The title bar text
3303 * @param {String} msg The message box body text
3304 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3305 * @param {Object} scope (optional) The scope of the callback function
3306 * @return {Roo.MessageBox} This message box
3308 alert : function(title, msg, fn, scope){
3321 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3322 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3323 * You are responsible for closing the message box when the process is complete.
3324 * @param {String} msg The message box body text
3325 * @param {String} title (optional) The title bar text
3326 * @return {Roo.MessageBox} This message box
3328 wait : function(msg, title){
3339 waitTimer = Roo.TaskMgr.start({
3341 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3349 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3350 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3351 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3352 * @param {String} title The title bar text
3353 * @param {String} msg The message box body text
3354 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3355 * @param {Object} scope (optional) The scope of the callback function
3356 * @return {Roo.MessageBox} This message box
3358 confirm : function(title, msg, fn, scope){
3362 buttons: this.YESNO,
3371 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3372 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3373 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3374 * (could also be the top-right close button) and the text that was entered will be passed as the two
3375 * parameters to the callback.
3376 * @param {String} title The title bar text
3377 * @param {String} msg The message box body text
3378 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3379 * @param {Object} scope (optional) The scope of the callback function
3380 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3381 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3382 * @return {Roo.MessageBox} This message box
3384 prompt : function(title, msg, fn, scope, multiline){
3388 buttons: this.OKCANCEL,
3393 multiline: multiline,
3400 * Button config that displays a single OK button
3405 * Button config that displays Yes and No buttons
3408 YESNO : {yes:true, no:true},
3410 * Button config that displays OK and Cancel buttons
3413 OKCANCEL : {ok:true, cancel:true},
3415 * Button config that displays Yes, No and Cancel buttons
3418 YESNOCANCEL : {yes:true, no:true, cancel:true},
3421 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3424 defaultTextHeight : 75,
3426 * The maximum width in pixels of the message box (defaults to 600)
3431 * The minimum width in pixels of the message box (defaults to 100)
3436 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3437 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3440 minProgressWidth : 250,
3442 * An object containing the default button text strings that can be overriden for localized language support.
3443 * Supported properties are: ok, cancel, yes and no.
3444 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3457 * Shorthand for {@link Roo.MessageBox}
3459 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3460 Roo.Msg = Roo.Msg || Roo.MessageBox;
3469 * @class Roo.bootstrap.Navbar
3470 * @extends Roo.bootstrap.Component
3471 * Bootstrap Navbar class
3474 * Create a new Navbar
3475 * @param {Object} config The config object
3479 Roo.bootstrap.Navbar = function(config){
3480 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3484 * @event beforetoggle
3485 * Fire before toggle the menu
3486 * @param {Roo.EventObject} e
3488 "beforetoggle" : true
3492 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3501 getAutoCreate : function(){
3504 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3508 initEvents :function ()
3510 //Roo.log(this.el.select('.navbar-toggle',true));
3511 this.el.select('.navbar-toggle',true).on('click', function() {
3512 if(this.fireEvent('beforetoggle', this) !== false){
3513 this.el.select('.navbar-collapse',true).toggleClass('in');
3523 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3525 var size = this.el.getSize();
3526 this.maskEl.setSize(size.width, size.height);
3527 this.maskEl.enableDisplayMode("block");
3536 getChildContainer : function()
3538 if (this.el.select('.collapse').getCount()) {
3539 return this.el.select('.collapse',true).first();
3572 * @class Roo.bootstrap.NavSimplebar
3573 * @extends Roo.bootstrap.Navbar
3574 * Bootstrap Sidebar class
3576 * @cfg {Boolean} inverse is inverted color
3578 * @cfg {String} type (nav | pills | tabs)
3579 * @cfg {Boolean} arrangement stacked | justified
3580 * @cfg {String} align (left | right) alignment
3582 * @cfg {Boolean} main (true|false) main nav bar? default false
3583 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3585 * @cfg {String} tag (header|footer|nav|div) default is nav
3591 * Create a new Sidebar
3592 * @param {Object} config The config object
3596 Roo.bootstrap.NavSimplebar = function(config){
3597 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3600 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3616 getAutoCreate : function(){
3620 tag : this.tag || 'div',
3633 this.type = this.type || 'nav';
3634 if (['tabs','pills'].indexOf(this.type)!==-1) {
3635 cfg.cn[0].cls += ' nav-' + this.type
3639 if (this.type!=='nav') {
3640 Roo.log('nav type must be nav/tabs/pills')
3642 cfg.cn[0].cls += ' navbar-nav'
3648 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3649 cfg.cn[0].cls += ' nav-' + this.arrangement;
3653 if (this.align === 'right') {
3654 cfg.cn[0].cls += ' navbar-right';
3658 cfg.cls += ' navbar-inverse';
3685 * @class Roo.bootstrap.NavHeaderbar
3686 * @extends Roo.bootstrap.NavSimplebar
3687 * Bootstrap Sidebar class
3689 * @cfg {String} brand what is brand
3690 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3691 * @cfg {String} brand_href href of the brand
3692 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3693 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3694 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3695 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3698 * Create a new Sidebar
3699 * @param {Object} config The config object
3703 Roo.bootstrap.NavHeaderbar = function(config){
3704 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3708 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3715 desktopCenter : false,
3718 getAutoCreate : function(){
3721 tag: this.nav || 'nav',
3728 if (this.desktopCenter) {
3729 cn.push({cls : 'container', cn : []});
3736 cls: 'navbar-header',
3741 cls: 'navbar-toggle',
3742 'data-toggle': 'collapse',
3747 html: 'Toggle navigation'
3769 cls: 'collapse navbar-collapse',
3773 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3775 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3776 cfg.cls += ' navbar-' + this.position;
3778 // tag can override this..
3780 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3783 if (this.brand !== '') {
3786 href: this.brand_href ? this.brand_href : '#',
3787 cls: 'navbar-brand',
3795 cfg.cls += ' main-nav';
3803 getHeaderChildContainer : function()
3805 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3806 return this.el.select('.navbar-header',true).first();
3809 return this.getChildContainer();
3813 initEvents : function()
3815 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3817 if (this.autohide) {
3822 Roo.get(document).on('scroll',function(e) {
3823 var ns = Roo.get(document).getScroll().top;
3824 var os = prevScroll;
3828 ft.removeClass('slideDown');
3829 ft.addClass('slideUp');
3832 ft.removeClass('slideUp');
3833 ft.addClass('slideDown');
3854 * @class Roo.bootstrap.NavSidebar
3855 * @extends Roo.bootstrap.Navbar
3856 * Bootstrap Sidebar class
3859 * Create a new Sidebar
3860 * @param {Object} config The config object
3864 Roo.bootstrap.NavSidebar = function(config){
3865 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3868 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3870 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3872 getAutoCreate : function(){
3877 cls: 'sidebar sidebar-nav'
3899 * @class Roo.bootstrap.NavGroup
3900 * @extends Roo.bootstrap.Component
3901 * Bootstrap NavGroup class
3902 * @cfg {String} align (left|right)
3903 * @cfg {Boolean} inverse
3904 * @cfg {String} type (nav|pills|tab) default nav
3905 * @cfg {String} navId - reference Id for navbar.
3909 * Create a new nav group
3910 * @param {Object} config The config object
3913 Roo.bootstrap.NavGroup = function(config){
3914 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3917 Roo.bootstrap.NavGroup.register(this);
3921 * Fires when the active item changes
3922 * @param {Roo.bootstrap.NavGroup} this
3923 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3924 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3931 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3942 getAutoCreate : function()
3944 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3951 if (['tabs','pills'].indexOf(this.type)!==-1) {
3952 cfg.cls += ' nav-' + this.type
3954 if (this.type!=='nav') {
3955 Roo.log('nav type must be nav/tabs/pills')
3957 cfg.cls += ' navbar-nav'
3960 if (this.parent().sidebar) {
3963 cls: 'dashboard-menu sidebar-menu'
3969 if (this.form === true) {
3975 if (this.align === 'right') {
3976 cfg.cls += ' navbar-right';
3978 cfg.cls += ' navbar-left';
3982 if (this.align === 'right') {
3983 cfg.cls += ' navbar-right';
3987 cfg.cls += ' navbar-inverse';
3995 * sets the active Navigation item
3996 * @param {Roo.bootstrap.NavItem} the new current navitem
3998 setActiveItem : function(item)
4001 Roo.each(this.navItems, function(v){
4006 v.setActive(false, true);
4013 item.setActive(true, true);
4014 this.fireEvent('changed', this, item, prev);
4019 * gets the active Navigation item
4020 * @return {Roo.bootstrap.NavItem} the current navitem
4022 getActive : function()
4026 Roo.each(this.navItems, function(v){
4037 indexOfNav : function()
4041 Roo.each(this.navItems, function(v,i){
4052 * adds a Navigation item
4053 * @param {Roo.bootstrap.NavItem} the navitem to add
4055 addItem : function(cfg)
4057 var cn = new Roo.bootstrap.NavItem(cfg);
4059 cn.parentId = this.id;
4060 cn.onRender(this.el, null);
4064 * register a Navigation item
4065 * @param {Roo.bootstrap.NavItem} the navitem to add
4067 register : function(item)
4069 this.navItems.push( item);
4070 item.navId = this.navId;
4075 * clear all the Navigation item
4078 clearAll : function()
4081 this.el.dom.innerHTML = '';
4084 getNavItem: function(tabId)
4087 Roo.each(this.navItems, function(e) {
4088 if (e.tabId == tabId) {
4098 setActiveNext : function()
4100 var i = this.indexOfNav(this.getActive());
4101 if (i > this.navItems.length) {
4104 this.setActiveItem(this.navItems[i+1]);
4106 setActivePrev : function()
4108 var i = this.indexOfNav(this.getActive());
4112 this.setActiveItem(this.navItems[i-1]);
4114 clearWasActive : function(except) {
4115 Roo.each(this.navItems, function(e) {
4116 if (e.tabId != except.tabId && e.was_active) {
4117 e.was_active = false;
4124 getWasActive : function ()
4127 Roo.each(this.navItems, function(e) {
4142 Roo.apply(Roo.bootstrap.NavGroup, {
4146 * register a Navigation Group
4147 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4149 register : function(navgrp)
4151 this.groups[navgrp.navId] = navgrp;
4155 * fetch a Navigation Group based on the navigation ID
4156 * @param {string} the navgroup to add
4157 * @returns {Roo.bootstrap.NavGroup} the navgroup
4159 get: function(navId) {
4160 if (typeof(this.groups[navId]) == 'undefined') {
4162 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4164 return this.groups[navId] ;
4179 * @class Roo.bootstrap.NavItem
4180 * @extends Roo.bootstrap.Component
4181 * Bootstrap Navbar.NavItem class
4182 * @cfg {String} href link to
4183 * @cfg {String} html content of button
4184 * @cfg {String} badge text inside badge
4185 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4186 * @cfg {String} glyphicon name of glyphicon
4187 * @cfg {String} icon name of font awesome icon
4188 * @cfg {Boolean} active Is item active
4189 * @cfg {Boolean} disabled Is item disabled
4191 * @cfg {Boolean} preventDefault (true | false) default false
4192 * @cfg {String} tabId the tab that this item activates.
4193 * @cfg {String} tagtype (a|span) render as a href or span?
4194 * @cfg {Boolean} animateRef (true|false) link to element default false
4197 * Create a new Navbar Item
4198 * @param {Object} config The config object
4200 Roo.bootstrap.NavItem = function(config){
4201 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4206 * The raw click event for the entire grid.
4207 * @param {Roo.EventObject} e
4212 * Fires when the active item active state changes
4213 * @param {Roo.bootstrap.NavItem} this
4214 * @param {boolean} state the new state
4220 * Fires when scroll to element
4221 * @param {Roo.bootstrap.NavItem} this
4222 * @param {Object} options
4223 * @param {Roo.EventObject} e
4231 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4239 preventDefault : false,
4246 getAutoCreate : function(){
4255 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4257 if (this.disabled) {
4258 cfg.cls += ' disabled';
4261 if (this.href || this.html || this.glyphicon || this.icon) {
4265 href : this.href || "#",
4266 html: this.html || ''
4271 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4274 if(this.glyphicon) {
4275 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4280 cfg.cn[0].html += " <span class='caret'></span>";
4284 if (this.badge !== '') {
4286 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4294 initEvents: function()
4296 if (typeof (this.menu) != 'undefined') {
4297 this.menu.parentType = this.xtype;
4298 this.menu.triggerEl = this.el;
4299 this.menu = this.addxtype(Roo.apply({}, this.menu));
4302 this.el.select('a',true).on('click', this.onClick, this);
4304 if(this.tagtype == 'span'){
4305 this.el.select('span',true).on('click', this.onClick, this);
4308 // at this point parent should be available..
4309 this.parent().register(this);
4312 onClick : function(e)
4314 if (e.getTarget('.dropdown-menu-item')) {
4315 // did you click on a menu itemm.... - then don't trigger onclick..
4320 this.preventDefault ||
4323 Roo.log("NavItem - prevent Default?");
4327 if (this.disabled) {
4331 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4332 if (tg && tg.transition) {
4333 Roo.log("waiting for the transitionend");
4339 //Roo.log("fire event clicked");
4340 if(this.fireEvent('click', this, e) === false){
4344 if(this.tagtype == 'span'){
4348 //Roo.log(this.href);
4349 var ael = this.el.select('a',true).first();
4352 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4353 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4354 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4355 return; // ignore... - it's a 'hash' to another page.
4357 Roo.log("NavItem - prevent Default?");
4359 this.scrollToElement(e);
4363 var p = this.parent();
4365 if (['tabs','pills'].indexOf(p.type)!==-1) {
4366 if (typeof(p.setActiveItem) !== 'undefined') {
4367 p.setActiveItem(this);
4371 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4372 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4373 // remove the collapsed menu expand...
4374 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4378 isActive: function () {
4381 setActive : function(state, fire, is_was_active)
4383 if (this.active && !state && this.navId) {
4384 this.was_active = true;
4385 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4387 nv.clearWasActive(this);
4391 this.active = state;
4394 this.el.removeClass('active');
4395 } else if (!this.el.hasClass('active')) {
4396 this.el.addClass('active');
4399 this.fireEvent('changed', this, state);
4402 // show a panel if it's registered and related..
4404 if (!this.navId || !this.tabId || !state || is_was_active) {
4408 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4412 var pan = tg.getPanelByName(this.tabId);
4416 // if we can not flip to new panel - go back to old nav highlight..
4417 if (false == tg.showPanel(pan)) {
4418 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4420 var onav = nv.getWasActive();
4422 onav.setActive(true, false, true);
4431 // this should not be here...
4432 setDisabled : function(state)
4434 this.disabled = state;
4436 this.el.removeClass('disabled');
4437 } else if (!this.el.hasClass('disabled')) {
4438 this.el.addClass('disabled');
4444 * Fetch the element to display the tooltip on.
4445 * @return {Roo.Element} defaults to this.el
4447 tooltipEl : function()
4449 return this.el.select('' + this.tagtype + '', true).first();
4452 scrollToElement : function(e)
4454 var c = document.body;
4457 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4459 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4460 c = document.documentElement;
4463 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4469 var o = target.calcOffsetsTo(c);
4476 this.fireEvent('scrollto', this, options, e);
4478 Roo.get(c).scrollTo('top', options.value, true);
4491 * <span> icon </span>
4492 * <span> text </span>
4493 * <span>badge </span>
4497 * @class Roo.bootstrap.NavSidebarItem
4498 * @extends Roo.bootstrap.NavItem
4499 * Bootstrap Navbar.NavSidebarItem class
4500 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4501 * {bool} open is the menu open
4503 * Create a new Navbar Button
4504 * @param {Object} config The config object
4506 Roo.bootstrap.NavSidebarItem = function(config){
4507 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4512 * The raw click event for the entire grid.
4513 * @param {Roo.EventObject} e
4518 * Fires when the active item active state changes
4519 * @param {Roo.bootstrap.NavSidebarItem} this
4520 * @param {boolean} state the new state
4528 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4530 badgeWeight : 'default',
4534 getAutoCreate : function(){
4539 href : this.href || '#',
4551 html : this.html || ''
4556 cfg.cls += ' active';
4559 if (this.disabled) {
4560 cfg.cls += ' disabled';
4563 cfg.cls += ' open x-open';
4566 if (this.glyphicon || this.icon) {
4567 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4568 a.cn.push({ tag : 'i', cls : c }) ;
4573 if (this.badge !== '') {
4575 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4579 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4580 a.cls += 'dropdown-toggle treeview' ;
4588 initEvents : function()
4590 if (typeof (this.menu) != 'undefined') {
4591 this.menu.parentType = this.xtype;
4592 this.menu.triggerEl = this.el;
4593 this.menu = this.addxtype(Roo.apply({}, this.menu));
4596 this.el.on('click', this.onClick, this);
4599 if(this.badge !== ''){
4601 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4606 onClick : function(e)
4613 if(this.preventDefault){
4617 this.fireEvent('click', this);
4620 disable : function()
4622 this.setDisabled(true);
4627 this.setDisabled(false);
4630 setDisabled : function(state)
4632 if(this.disabled == state){
4636 this.disabled = state;
4639 this.el.addClass('disabled');
4643 this.el.removeClass('disabled');
4648 setActive : function(state)
4650 if(this.active == state){
4654 this.active = state;
4657 this.el.addClass('active');
4661 this.el.removeClass('active');
4666 isActive: function ()
4671 setBadge : function(str)
4677 this.badgeEl.dom.innerHTML = str;
4694 * @class Roo.bootstrap.Row
4695 * @extends Roo.bootstrap.Component
4696 * Bootstrap Row class (contains columns...)
4700 * @param {Object} config The config object
4703 Roo.bootstrap.Row = function(config){
4704 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4707 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4709 getAutoCreate : function(){
4728 * @class Roo.bootstrap.Element
4729 * @extends Roo.bootstrap.Component
4730 * Bootstrap Element class
4731 * @cfg {String} html contents of the element
4732 * @cfg {String} tag tag of the element
4733 * @cfg {String} cls class of the element
4734 * @cfg {Boolean} preventDefault (true|false) default false
4735 * @cfg {Boolean} clickable (true|false) default false
4738 * Create a new Element
4739 * @param {Object} config The config object
4742 Roo.bootstrap.Element = function(config){
4743 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4749 * When a element is chick
4750 * @param {Roo.bootstrap.Element} this
4751 * @param {Roo.EventObject} e
4757 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4762 preventDefault: false,
4765 getAutoCreate : function(){
4776 initEvents: function()
4778 Roo.bootstrap.Element.superclass.initEvents.call(this);
4781 this.el.on('click', this.onClick, this);
4786 onClick : function(e)
4788 if(this.preventDefault){
4792 this.fireEvent('click', this, e);
4795 getValue : function()
4797 return this.el.dom.innerHTML;
4800 setValue : function(value)
4802 this.el.dom.innerHTML = value;
4817 * @class Roo.bootstrap.Pagination
4818 * @extends Roo.bootstrap.Component
4819 * Bootstrap Pagination class
4820 * @cfg {String} size xs | sm | md | lg
4821 * @cfg {Boolean} inverse false | true
4824 * Create a new Pagination
4825 * @param {Object} config The config object
4828 Roo.bootstrap.Pagination = function(config){
4829 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4832 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4838 getAutoCreate : function(){
4844 cfg.cls += ' inverse';
4850 cfg.cls += " " + this.cls;
4868 * @class Roo.bootstrap.PaginationItem
4869 * @extends Roo.bootstrap.Component
4870 * Bootstrap PaginationItem class
4871 * @cfg {String} html text
4872 * @cfg {String} href the link
4873 * @cfg {Boolean} preventDefault (true | false) default true
4874 * @cfg {Boolean} active (true | false) default false
4875 * @cfg {Boolean} disabled default false
4879 * Create a new PaginationItem
4880 * @param {Object} config The config object
4884 Roo.bootstrap.PaginationItem = function(config){
4885 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4890 * The raw click event for the entire grid.
4891 * @param {Roo.EventObject} e
4897 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4901 preventDefault: true,
4906 getAutoCreate : function(){
4912 href : this.href ? this.href : '#',
4913 html : this.html ? this.html : ''
4923 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4927 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4933 initEvents: function() {
4935 this.el.on('click', this.onClick, this);
4938 onClick : function(e)
4940 Roo.log('PaginationItem on click ');
4941 if(this.preventDefault){
4949 this.fireEvent('click', this, e);
4965 * @class Roo.bootstrap.Slider
4966 * @extends Roo.bootstrap.Component
4967 * Bootstrap Slider class
4970 * Create a new Slider
4971 * @param {Object} config The config object
4974 Roo.bootstrap.Slider = function(config){
4975 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4978 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4980 getAutoCreate : function(){
4984 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4988 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5000 * Ext JS Library 1.1.1
5001 * Copyright(c) 2006-2007, Ext JS, LLC.
5003 * Originally Released Under LGPL - original licence link has changed is not relivant.
5006 * <script type="text/javascript">
5011 * @class Roo.grid.ColumnModel
5012 * @extends Roo.util.Observable
5013 * This is the default implementation of a ColumnModel used by the Grid. It defines
5014 * the columns in the grid.
5017 var colModel = new Roo.grid.ColumnModel([
5018 {header: "Ticker", width: 60, sortable: true, locked: true},
5019 {header: "Company Name", width: 150, sortable: true},
5020 {header: "Market Cap.", width: 100, sortable: true},
5021 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5022 {header: "Employees", width: 100, sortable: true, resizable: false}
5027 * The config options listed for this class are options which may appear in each
5028 * individual column definition.
5029 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5031 * @param {Object} config An Array of column config objects. See this class's
5032 * config objects for details.
5034 Roo.grid.ColumnModel = function(config){
5036 * The config passed into the constructor
5038 this.config = config;
5041 // if no id, create one
5042 // if the column does not have a dataIndex mapping,
5043 // map it to the order it is in the config
5044 for(var i = 0, len = config.length; i < len; i++){
5046 if(typeof c.dataIndex == "undefined"){
5049 if(typeof c.renderer == "string"){
5050 c.renderer = Roo.util.Format[c.renderer];
5052 if(typeof c.id == "undefined"){
5055 if(c.editor && c.editor.xtype){
5056 c.editor = Roo.factory(c.editor, Roo.grid);
5058 if(c.editor && c.editor.isFormField){
5059 c.editor = new Roo.grid.GridEditor(c.editor);
5061 this.lookup[c.id] = c;
5065 * The width of columns which have no width specified (defaults to 100)
5068 this.defaultWidth = 100;
5071 * Default sortable of columns which have no sortable specified (defaults to false)
5074 this.defaultSortable = false;
5078 * @event widthchange
5079 * Fires when the width of a column changes.
5080 * @param {ColumnModel} this
5081 * @param {Number} columnIndex The column index
5082 * @param {Number} newWidth The new width
5084 "widthchange": true,
5086 * @event headerchange
5087 * Fires when the text of a header changes.
5088 * @param {ColumnModel} this
5089 * @param {Number} columnIndex The column index
5090 * @param {Number} newText The new header text
5092 "headerchange": true,
5094 * @event hiddenchange
5095 * Fires when a column is hidden or "unhidden".
5096 * @param {ColumnModel} this
5097 * @param {Number} columnIndex The column index
5098 * @param {Boolean} hidden true if hidden, false otherwise
5100 "hiddenchange": true,
5102 * @event columnmoved
5103 * Fires when a column is moved.
5104 * @param {ColumnModel} this
5105 * @param {Number} oldIndex
5106 * @param {Number} newIndex
5108 "columnmoved" : true,
5110 * @event columlockchange
5111 * Fires when a column's locked state is changed
5112 * @param {ColumnModel} this
5113 * @param {Number} colIndex
5114 * @param {Boolean} locked true if locked
5116 "columnlockchange" : true
5118 Roo.grid.ColumnModel.superclass.constructor.call(this);
5120 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5122 * @cfg {String} header The header text to display in the Grid view.
5125 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5126 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5127 * specified, the column's index is used as an index into the Record's data Array.
5130 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5131 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5134 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5135 * Defaults to the value of the {@link #defaultSortable} property.
5136 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5139 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5142 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5145 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5148 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5151 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5152 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5153 * default renderer uses the raw data value. If an object is returned (bootstrap only)
5154 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5157 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5160 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5163 * @cfg {String} cursor (Optional)
5166 * @cfg {String} tooltip (Optional)
5169 * @cfg {Number} xs (Optional)
5172 * @cfg {Number} sm (Optional)
5175 * @cfg {Number} md (Optional)
5178 * @cfg {Number} lg (Optional)
5181 * Returns the id of the column at the specified index.
5182 * @param {Number} index The column index
5183 * @return {String} the id
5185 getColumnId : function(index){
5186 return this.config[index].id;
5190 * Returns the column for a specified id.
5191 * @param {String} id The column id
5192 * @return {Object} the column
5194 getColumnById : function(id){
5195 return this.lookup[id];
5200 * Returns the column for a specified dataIndex.
5201 * @param {String} dataIndex The column dataIndex
5202 * @return {Object|Boolean} the column or false if not found
5204 getColumnByDataIndex: function(dataIndex){
5205 var index = this.findColumnIndex(dataIndex);
5206 return index > -1 ? this.config[index] : false;
5210 * Returns the index for a specified column id.
5211 * @param {String} id The column id
5212 * @return {Number} the index, or -1 if not found
5214 getIndexById : function(id){
5215 for(var i = 0, len = this.config.length; i < len; i++){
5216 if(this.config[i].id == id){
5224 * Returns the index for a specified column dataIndex.
5225 * @param {String} dataIndex The column dataIndex
5226 * @return {Number} the index, or -1 if not found
5229 findColumnIndex : function(dataIndex){
5230 for(var i = 0, len = this.config.length; i < len; i++){
5231 if(this.config[i].dataIndex == dataIndex){
5239 moveColumn : function(oldIndex, newIndex){
5240 var c = this.config[oldIndex];
5241 this.config.splice(oldIndex, 1);
5242 this.config.splice(newIndex, 0, c);
5243 this.dataMap = null;
5244 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5247 isLocked : function(colIndex){
5248 return this.config[colIndex].locked === true;
5251 setLocked : function(colIndex, value, suppressEvent){
5252 if(this.isLocked(colIndex) == value){
5255 this.config[colIndex].locked = value;
5257 this.fireEvent("columnlockchange", this, colIndex, value);
5261 getTotalLockedWidth : function(){
5263 for(var i = 0; i < this.config.length; i++){
5264 if(this.isLocked(i) && !this.isHidden(i)){
5265 this.totalWidth += this.getColumnWidth(i);
5271 getLockedCount : function(){
5272 for(var i = 0, len = this.config.length; i < len; i++){
5273 if(!this.isLocked(i)){
5278 return this.config.length;
5282 * Returns the number of columns.
5285 getColumnCount : function(visibleOnly){
5286 if(visibleOnly === true){
5288 for(var i = 0, len = this.config.length; i < len; i++){
5289 if(!this.isHidden(i)){
5295 return this.config.length;
5299 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5300 * @param {Function} fn
5301 * @param {Object} scope (optional)
5302 * @return {Array} result
5304 getColumnsBy : function(fn, scope){
5306 for(var i = 0, len = this.config.length; i < len; i++){
5307 var c = this.config[i];
5308 if(fn.call(scope||this, c, i) === true){
5316 * Returns true if the specified column is sortable.
5317 * @param {Number} col The column index
5320 isSortable : function(col){
5321 if(typeof this.config[col].sortable == "undefined"){
5322 return this.defaultSortable;
5324 return this.config[col].sortable;
5328 * Returns the rendering (formatting) function defined for the column.
5329 * @param {Number} col The column index.
5330 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5332 getRenderer : function(col){
5333 if(!this.config[col].renderer){
5334 return Roo.grid.ColumnModel.defaultRenderer;
5336 return this.config[col].renderer;
5340 * Sets the rendering (formatting) function for a column.
5341 * @param {Number} col The column index
5342 * @param {Function} fn The function to use to process the cell's raw data
5343 * to return HTML markup for the grid view. The render function is called with
5344 * the following parameters:<ul>
5345 * <li>Data value.</li>
5346 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5347 * <li>css A CSS style string to apply to the table cell.</li>
5348 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5349 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5350 * <li>Row index</li>
5351 * <li>Column index</li>
5352 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5354 setRenderer : function(col, fn){
5355 this.config[col].renderer = fn;
5359 * Returns the width for the specified column.
5360 * @param {Number} col The column index
5363 getColumnWidth : function(col){
5364 return this.config[col].width * 1 || this.defaultWidth;
5368 * Sets the width for a column.
5369 * @param {Number} col The column index
5370 * @param {Number} width The new width
5372 setColumnWidth : function(col, width, suppressEvent){
5373 this.config[col].width = width;
5374 this.totalWidth = null;
5376 this.fireEvent("widthchange", this, col, width);
5381 * Returns the total width of all columns.
5382 * @param {Boolean} includeHidden True to include hidden column widths
5385 getTotalWidth : function(includeHidden){
5386 if(!this.totalWidth){
5387 this.totalWidth = 0;
5388 for(var i = 0, len = this.config.length; i < len; i++){
5389 if(includeHidden || !this.isHidden(i)){
5390 this.totalWidth += this.getColumnWidth(i);
5394 return this.totalWidth;
5398 * Returns the header for the specified column.
5399 * @param {Number} col The column index
5402 getColumnHeader : function(col){
5403 return this.config[col].header;
5407 * Sets the header for a column.
5408 * @param {Number} col The column index
5409 * @param {String} header The new header
5411 setColumnHeader : function(col, header){
5412 this.config[col].header = header;
5413 this.fireEvent("headerchange", this, col, header);
5417 * Returns the tooltip for the specified column.
5418 * @param {Number} col The column index
5421 getColumnTooltip : function(col){
5422 return this.config[col].tooltip;
5425 * Sets the tooltip for a column.
5426 * @param {Number} col The column index
5427 * @param {String} tooltip The new tooltip
5429 setColumnTooltip : function(col, tooltip){
5430 this.config[col].tooltip = tooltip;
5434 * Returns the dataIndex for the specified column.
5435 * @param {Number} col The column index
5438 getDataIndex : function(col){
5439 return this.config[col].dataIndex;
5443 * Sets the dataIndex for a column.
5444 * @param {Number} col The column index
5445 * @param {Number} dataIndex The new dataIndex
5447 setDataIndex : function(col, dataIndex){
5448 this.config[col].dataIndex = dataIndex;
5454 * Returns true if the cell is editable.
5455 * @param {Number} colIndex The column index
5456 * @param {Number} rowIndex The row index - this is nto actually used..?
5459 isCellEditable : function(colIndex, rowIndex){
5460 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5464 * Returns the editor defined for the cell/column.
5465 * return false or null to disable editing.
5466 * @param {Number} colIndex The column index
5467 * @param {Number} rowIndex The row index
5470 getCellEditor : function(colIndex, rowIndex){
5471 return this.config[colIndex].editor;
5475 * Sets if a column is editable.
5476 * @param {Number} col The column index
5477 * @param {Boolean} editable True if the column is editable
5479 setEditable : function(col, editable){
5480 this.config[col].editable = editable;
5485 * Returns true if the column is hidden.
5486 * @param {Number} colIndex The column index
5489 isHidden : function(colIndex){
5490 return this.config[colIndex].hidden;
5495 * Returns true if the column width cannot be changed
5497 isFixed : function(colIndex){
5498 return this.config[colIndex].fixed;
5502 * Returns true if the column can be resized
5505 isResizable : function(colIndex){
5506 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5509 * Sets if a column is hidden.
5510 * @param {Number} colIndex The column index
5511 * @param {Boolean} hidden True if the column is hidden
5513 setHidden : function(colIndex, hidden){
5514 this.config[colIndex].hidden = hidden;
5515 this.totalWidth = null;
5516 this.fireEvent("hiddenchange", this, colIndex, hidden);
5520 * Sets the editor for a column.
5521 * @param {Number} col The column index
5522 * @param {Object} editor The editor object
5524 setEditor : function(col, editor){
5525 this.config[col].editor = editor;
5529 Roo.grid.ColumnModel.defaultRenderer = function(value){
5530 if(typeof value == "string" && value.length < 1){
5536 // Alias for backwards compatibility
5537 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5540 * Ext JS Library 1.1.1
5541 * Copyright(c) 2006-2007, Ext JS, LLC.
5543 * Originally Released Under LGPL - original licence link has changed is not relivant.
5546 * <script type="text/javascript">
5550 * @class Roo.LoadMask
5551 * A simple utility class for generically masking elements while loading data. If the element being masked has
5552 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5553 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5554 * element's UpdateManager load indicator and will be destroyed after the initial load.
5556 * Create a new LoadMask
5557 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5558 * @param {Object} config The config object
5560 Roo.LoadMask = function(el, config){
5561 this.el = Roo.get(el);
5562 Roo.apply(this, config);
5564 this.store.on('beforeload', this.onBeforeLoad, this);
5565 this.store.on('load', this.onLoad, this);
5566 this.store.on('loadexception', this.onLoadException, this);
5567 this.removeMask = false;
5569 var um = this.el.getUpdateManager();
5570 um.showLoadIndicator = false; // disable the default indicator
5571 um.on('beforeupdate', this.onBeforeLoad, this);
5572 um.on('update', this.onLoad, this);
5573 um.on('failure', this.onLoad, this);
5574 this.removeMask = true;
5578 Roo.LoadMask.prototype = {
5580 * @cfg {Boolean} removeMask
5581 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5582 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5586 * The text to display in a centered loading message box (defaults to 'Loading...')
5590 * @cfg {String} msgCls
5591 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5593 msgCls : 'x-mask-loading',
5596 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5602 * Disables the mask to prevent it from being displayed
5604 disable : function(){
5605 this.disabled = true;
5609 * Enables the mask so that it can be displayed
5611 enable : function(){
5612 this.disabled = false;
5615 onLoadException : function()
5619 if (typeof(arguments[3]) != 'undefined') {
5620 Roo.MessageBox.alert("Error loading",arguments[3]);
5624 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5625 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5634 this.el.unmask(this.removeMask);
5639 this.el.unmask(this.removeMask);
5643 onBeforeLoad : function(){
5645 this.el.mask(this.msg, this.msgCls);
5650 destroy : function(){
5652 this.store.un('beforeload', this.onBeforeLoad, this);
5653 this.store.un('load', this.onLoad, this);
5654 this.store.un('loadexception', this.onLoadException, this);
5656 var um = this.el.getUpdateManager();
5657 um.un('beforeupdate', this.onBeforeLoad, this);
5658 um.un('update', this.onLoad, this);
5659 um.un('failure', this.onLoad, this);
5670 * @class Roo.bootstrap.Table
5671 * @extends Roo.bootstrap.Component
5672 * Bootstrap Table class
5673 * @cfg {String} cls table class
5674 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5675 * @cfg {String} bgcolor Specifies the background color for a table
5676 * @cfg {Number} border Specifies whether the table cells should have borders or not
5677 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5678 * @cfg {Number} cellspacing Specifies the space between cells
5679 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5680 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5681 * @cfg {String} sortable Specifies that the table should be sortable
5682 * @cfg {String} summary Specifies a summary of the content of a table
5683 * @cfg {Number} width Specifies the width of a table
5684 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5686 * @cfg {boolean} striped Should the rows be alternative striped
5687 * @cfg {boolean} bordered Add borders to the table
5688 * @cfg {boolean} hover Add hover highlighting
5689 * @cfg {boolean} condensed Format condensed
5690 * @cfg {boolean} responsive Format condensed
5691 * @cfg {Boolean} loadMask (true|false) default false
5692 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5693 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5694 * @cfg {Boolean} rowSelection (true|false) default false
5695 * @cfg {Boolean} cellSelection (true|false) default false
5696 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5697 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5701 * Create a new Table
5702 * @param {Object} config The config object
5705 Roo.bootstrap.Table = function(config){
5706 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5711 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5712 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5713 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5714 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5718 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5719 this.sm = this.selModel;
5720 this.sm.xmodule = this.xmodule || false;
5722 if (this.cm && typeof(this.cm.config) == 'undefined') {
5723 this.colModel = new Roo.grid.ColumnModel(this.cm);
5724 this.cm = this.colModel;
5725 this.cm.xmodule = this.xmodule || false;
5728 this.store= Roo.factory(this.store, Roo.data);
5729 this.ds = this.store;
5730 this.ds.xmodule = this.xmodule || false;
5733 if (this.footer && this.store) {
5734 this.footer.dataSource = this.ds;
5735 this.footer = Roo.factory(this.footer);
5742 * Fires when a cell is clicked
5743 * @param {Roo.bootstrap.Table} this
5744 * @param {Roo.Element} el
5745 * @param {Number} rowIndex
5746 * @param {Number} columnIndex
5747 * @param {Roo.EventObject} e
5751 * @event celldblclick
5752 * Fires when a cell is double clicked
5753 * @param {Roo.bootstrap.Table} this
5754 * @param {Roo.Element} el
5755 * @param {Number} rowIndex
5756 * @param {Number} columnIndex
5757 * @param {Roo.EventObject} e
5759 "celldblclick" : true,
5762 * Fires when a row is clicked
5763 * @param {Roo.bootstrap.Table} this
5764 * @param {Roo.Element} el
5765 * @param {Number} rowIndex
5766 * @param {Roo.EventObject} e
5770 * @event rowdblclick
5771 * Fires when a row is double clicked
5772 * @param {Roo.bootstrap.Table} this
5773 * @param {Roo.Element} el
5774 * @param {Number} rowIndex
5775 * @param {Roo.EventObject} e
5777 "rowdblclick" : true,
5780 * Fires when a mouseover occur
5781 * @param {Roo.bootstrap.Table} this
5782 * @param {Roo.Element} el
5783 * @param {Number} rowIndex
5784 * @param {Number} columnIndex
5785 * @param {Roo.EventObject} e
5790 * Fires when a mouseout occur
5791 * @param {Roo.bootstrap.Table} this
5792 * @param {Roo.Element} el
5793 * @param {Number} rowIndex
5794 * @param {Number} columnIndex
5795 * @param {Roo.EventObject} e
5800 * Fires when a row is rendered, so you can change add a style to it.
5801 * @param {Roo.bootstrap.Table} this
5802 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5806 * @event rowsrendered
5807 * Fires when all the rows have been rendered
5808 * @param {Roo.bootstrap.Table} this
5810 'rowsrendered' : true,
5812 * @event contextmenu
5813 * The raw contextmenu event for the entire grid.
5814 * @param {Roo.EventObject} e
5816 "contextmenu" : true,
5818 * @event rowcontextmenu
5819 * Fires when a row is right clicked
5820 * @param {Roo.bootstrap.Table} this
5821 * @param {Number} rowIndex
5822 * @param {Roo.EventObject} e
5824 "rowcontextmenu" : true,
5826 * @event cellcontextmenu
5827 * Fires when a cell is right clicked
5828 * @param {Roo.bootstrap.Table} this
5829 * @param {Number} rowIndex
5830 * @param {Number} cellIndex
5831 * @param {Roo.EventObject} e
5833 "cellcontextmenu" : true,
5835 * @event headercontextmenu
5836 * Fires when a header is right clicked
5837 * @param {Roo.bootstrap.Table} this
5838 * @param {Number} columnIndex
5839 * @param {Roo.EventObject} e
5841 "headercontextmenu" : true
5845 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5871 rowSelection : false,
5872 cellSelection : false,
5875 // Roo.Element - the tbody
5877 // Roo.Element - thead element
5880 container: false, // used by gridpanel...
5882 getAutoCreate : function()
5884 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5891 if (this.scrollBody) {
5892 cfg.cls += ' table-body-fixed';
5895 cfg.cls += ' table-striped';
5899 cfg.cls += ' table-hover';
5901 if (this.bordered) {
5902 cfg.cls += ' table-bordered';
5904 if (this.condensed) {
5905 cfg.cls += ' table-condensed';
5907 if (this.responsive) {
5908 cfg.cls += ' table-responsive';
5912 cfg.cls+= ' ' +this.cls;
5915 // this lot should be simplifed...
5918 cfg.align=this.align;
5921 cfg.bgcolor=this.bgcolor;
5924 cfg.border=this.border;
5926 if (this.cellpadding) {
5927 cfg.cellpadding=this.cellpadding;
5929 if (this.cellspacing) {
5930 cfg.cellspacing=this.cellspacing;
5933 cfg.frame=this.frame;
5936 cfg.rules=this.rules;
5938 if (this.sortable) {
5939 cfg.sortable=this.sortable;
5942 cfg.summary=this.summary;
5945 cfg.width=this.width;
5948 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5951 if(this.store || this.cm){
5952 if(this.headerShow){
5953 cfg.cn.push(this.renderHeader());
5956 cfg.cn.push(this.renderBody());
5958 if(this.footerShow){
5959 cfg.cn.push(this.renderFooter());
5961 // where does this come from?
5962 //cfg.cls+= ' TableGrid';
5965 return { cn : [ cfg ] };
5968 initEvents : function()
5970 if(!this.store || !this.cm){
5974 //Roo.log('initEvents with ds!!!!');
5976 this.mainBody = this.el.select('tbody', true).first();
5977 this.mainHead = this.el.select('thead', true).first();
5983 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5984 e.on('click', _this.sort, _this);
5987 this.el.on("click", this.onClick, this);
5988 this.el.on("dblclick", this.onDblClick, this);
5990 // why is this done????? = it breaks dialogs??
5991 //this.parent().el.setStyle('position', 'relative');
5995 this.footer.parentId = this.id;
5996 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5999 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6001 this.store.on('load', this.onLoad, this);
6002 this.store.on('beforeload', this.onBeforeLoad, this);
6003 this.store.on('update', this.onUpdate, this);
6004 this.store.on('add', this.onAdd, this);
6005 this.store.on("clear", this.clear, this);
6007 this.el.on("contextmenu", this.onContextMenu, this);
6009 this.mainBody.on('scroll', this.onBodyScroll, this);
6014 onContextMenu : function(e, t)
6016 this.processEvent("contextmenu", e);
6019 processEvent : function(name, e)
6021 if (name != 'touchstart' ) {
6022 this.fireEvent(name, e);
6025 var t = e.getTarget();
6027 var cell = Roo.get(t);
6033 if(cell.findParent('tfoot', false, true)){
6037 if(cell.findParent('thead', false, true)){
6039 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6040 cell = Roo.get(t).findParent('th', false, true);
6042 Roo.log("failed to find th in thead?");
6043 Roo.log(e.getTarget());
6048 var cellIndex = cell.dom.cellIndex;
6050 var ename = name == 'touchstart' ? 'click' : name;
6051 this.fireEvent("header" + ename, this, cellIndex, e);
6056 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6057 cell = Roo.get(t).findParent('td', false, true);
6059 Roo.log("failed to find th in tbody?");
6060 Roo.log(e.getTarget());
6065 var row = cell.findParent('tr', false, true);
6066 var cellIndex = cell.dom.cellIndex;
6067 var rowIndex = row.dom.rowIndex - 1;
6071 this.fireEvent("row" + name, this, rowIndex, e);
6075 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6081 onMouseover : function(e, el)
6083 var cell = Roo.get(el);
6089 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6090 cell = cell.findParent('td', false, true);
6093 var row = cell.findParent('tr', false, true);
6094 var cellIndex = cell.dom.cellIndex;
6095 var rowIndex = row.dom.rowIndex - 1; // start from 0
6097 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6101 onMouseout : function(e, el)
6103 var cell = Roo.get(el);
6109 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6110 cell = cell.findParent('td', false, true);
6113 var row = cell.findParent('tr', false, true);
6114 var cellIndex = cell.dom.cellIndex;
6115 var rowIndex = row.dom.rowIndex - 1; // start from 0
6117 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6121 onClick : function(e, el)
6123 var cell = Roo.get(el);
6125 if(!cell || (!this.cellSelection && !this.rowSelection)){
6129 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6130 cell = cell.findParent('td', false, true);
6133 if(!cell || typeof(cell) == 'undefined'){
6137 var row = cell.findParent('tr', false, true);
6139 if(!row || typeof(row) == 'undefined'){
6143 var cellIndex = cell.dom.cellIndex;
6144 var rowIndex = this.getRowIndex(row);
6146 // why??? - should these not be based on SelectionModel?
6147 if(this.cellSelection){
6148 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6151 if(this.rowSelection){
6152 this.fireEvent('rowclick', this, row, rowIndex, e);
6158 onDblClick : function(e,el)
6160 var cell = Roo.get(el);
6162 if(!cell || (!this.CellSelection && !this.RowSelection)){
6166 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6167 cell = cell.findParent('td', false, true);
6170 if(!cell || typeof(cell) == 'undefined'){
6174 var row = cell.findParent('tr', false, true);
6176 if(!row || typeof(row) == 'undefined'){
6180 var cellIndex = cell.dom.cellIndex;
6181 var rowIndex = this.getRowIndex(row);
6183 if(this.CellSelection){
6184 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6187 if(this.RowSelection){
6188 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6192 sort : function(e,el)
6194 var col = Roo.get(el);
6196 if(!col.hasClass('sortable')){
6200 var sort = col.attr('sort');
6203 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6207 this.store.sortInfo = {field : sort, direction : dir};
6210 Roo.log("calling footer first");
6211 this.footer.onClick('first');
6214 this.store.load({ params : { start : 0 } });
6218 renderHeader : function()
6226 this.totalWidth = 0;
6228 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6230 var config = cm.config[i];
6235 html: cm.getColumnHeader(i)
6240 if(typeof(config.sortable) != 'undefined' && config.sortable){
6242 c.html = '<i class="glyphicon"></i>' + c.html;
6245 if(typeof(config.lgHeader) != 'undefined'){
6246 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6249 if(typeof(config.mdHeader) != 'undefined'){
6250 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6253 if(typeof(config.smHeader) != 'undefined'){
6254 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6257 if(typeof(config.xsHeader) != 'undefined'){
6258 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6265 if(typeof(config.tooltip) != 'undefined'){
6266 c.tooltip = config.tooltip;
6269 if(typeof(config.colspan) != 'undefined'){
6270 c.colspan = config.colspan;
6273 if(typeof(config.hidden) != 'undefined' && config.hidden){
6274 c.style += ' display:none;';
6277 if(typeof(config.dataIndex) != 'undefined'){
6278 c.sort = config.dataIndex;
6283 if(typeof(config.align) != 'undefined' && config.align.length){
6284 c.style += ' text-align:' + config.align + ';';
6287 if(typeof(config.width) != 'undefined'){
6288 c.style += ' width:' + config.width + 'px;';
6289 this.totalWidth += config.width;
6291 this.totalWidth += 100; // assume minimum of 100 per column?
6294 if(typeof(config.cls) != 'undefined'){
6295 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6298 ['xs','sm','md','lg'].map(function(size){
6300 if(typeof(config[size]) == 'undefined'){
6304 if (!config[size]) { // 0 = hidden
6305 c.cls += ' hidden-' + size;
6309 c.cls += ' col-' + size + '-' + config[size];
6319 renderBody : function()
6329 colspan : this.cm.getColumnCount()
6339 renderFooter : function()
6349 colspan : this.cm.getColumnCount()
6363 // Roo.log('ds onload');
6368 var ds = this.store;
6370 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6371 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6372 if (_this.store.sortInfo) {
6374 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6375 e.select('i', true).addClass(['glyphicon-arrow-up']);
6378 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6379 e.select('i', true).addClass(['glyphicon-arrow-down']);
6384 var tbody = this.mainBody;
6386 if(ds.getCount() > 0){
6387 ds.data.each(function(d,rowIndex){
6388 var row = this.renderRow(cm, ds, rowIndex);
6390 tbody.createChild(row);
6394 if(row.cellObjects.length){
6395 Roo.each(row.cellObjects, function(r){
6396 _this.renderCellObject(r);
6403 Roo.each(this.el.select('tbody td', true).elements, function(e){
6404 e.on('mouseover', _this.onMouseover, _this);
6407 Roo.each(this.el.select('tbody td', true).elements, function(e){
6408 e.on('mouseout', _this.onMouseout, _this);
6410 this.fireEvent('rowsrendered', this);
6411 //if(this.loadMask){
6412 // this.maskEl.hide();
6419 onUpdate : function(ds,record)
6421 this.refreshRow(record);
6424 onRemove : function(ds, record, index, isUpdate){
6425 if(isUpdate !== true){
6426 this.fireEvent("beforerowremoved", this, index, record);
6428 var bt = this.mainBody.dom;
6430 var rows = this.el.select('tbody > tr', true).elements;
6432 if(typeof(rows[index]) != 'undefined'){
6433 bt.removeChild(rows[index].dom);
6436 // if(bt.rows[index]){
6437 // bt.removeChild(bt.rows[index]);
6440 if(isUpdate !== true){
6441 //this.stripeRows(index);
6442 //this.syncRowHeights(index, index);
6444 this.fireEvent("rowremoved", this, index, record);
6448 onAdd : function(ds, records, rowIndex)
6450 //Roo.log('on Add called');
6451 // - note this does not handle multiple adding very well..
6452 var bt = this.mainBody.dom;
6453 for (var i =0 ; i < records.length;i++) {
6454 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6455 //Roo.log(records[i]);
6456 //Roo.log(this.store.getAt(rowIndex+i));
6457 this.insertRow(this.store, rowIndex + i, false);
6464 refreshRow : function(record){
6465 var ds = this.store, index;
6466 if(typeof record == 'number'){
6468 record = ds.getAt(index);
6470 index = ds.indexOf(record);
6472 this.insertRow(ds, index, true);
6473 this.onRemove(ds, record, index+1, true);
6474 //this.syncRowHeights(index, index);
6476 this.fireEvent("rowupdated", this, index, record);
6479 insertRow : function(dm, rowIndex, isUpdate){
6482 this.fireEvent("beforerowsinserted", this, rowIndex);
6484 //var s = this.getScrollState();
6485 var row = this.renderRow(this.cm, this.store, rowIndex);
6486 // insert before rowIndex..
6487 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6491 if(row.cellObjects.length){
6492 Roo.each(row.cellObjects, function(r){
6493 _this.renderCellObject(r);
6498 this.fireEvent("rowsinserted", this, rowIndex);
6499 //this.syncRowHeights(firstRow, lastRow);
6500 //this.stripeRows(firstRow);
6507 getRowDom : function(rowIndex)
6509 var rows = this.el.select('tbody > tr', true).elements;
6511 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6514 // returns the object tree for a tr..
6517 renderRow : function(cm, ds, rowIndex)
6520 var d = ds.getAt(rowIndex);
6527 var cellObjects = [];
6529 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6530 var config = cm.config[i];
6532 var renderer = cm.getRenderer(i);
6536 if(typeof(renderer) !== 'undefined'){
6537 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6539 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6540 // and are rendered into the cells after the row is rendered - using the id for the element.
6542 if(typeof(value) === 'object'){
6552 rowIndex : rowIndex,
6557 this.fireEvent('rowclass', this, rowcfg);
6561 cls : rowcfg.rowClass,
6563 html: (typeof(value) === 'object') ? '' : value
6570 if(typeof(config.colspan) != 'undefined'){
6571 td.colspan = config.colspan;
6574 if(typeof(config.hidden) != 'undefined' && config.hidden){
6575 td.style += ' display:none;';
6578 if(typeof(config.align) != 'undefined' && config.align.length){
6579 td.style += ' text-align:' + config.align + ';';
6582 if(typeof(config.width) != 'undefined'){
6583 td.style += ' width:' + config.width + 'px;';
6586 if(typeof(config.cursor) != 'undefined'){
6587 td.style += ' cursor:' + config.cursor + ';';
6590 if(typeof(config.cls) != 'undefined'){
6591 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6594 ['xs','sm','md','lg'].map(function(size){
6596 if(typeof(config[size]) == 'undefined'){
6600 if (!config[size]) { // 0 = hidden
6601 td.cls += ' hidden-' + size;
6605 td.cls += ' col-' + size + '-' + config[size];
6613 row.cellObjects = cellObjects;
6621 onBeforeLoad : function()
6623 //Roo.log('ds onBeforeLoad');
6627 //if(this.loadMask){
6628 // this.maskEl.show();
6636 this.el.select('tbody', true).first().dom.innerHTML = '';
6639 * Show or hide a row.
6640 * @param {Number} rowIndex to show or hide
6641 * @param {Boolean} state hide
6643 setRowVisibility : function(rowIndex, state)
6645 var bt = this.mainBody.dom;
6647 var rows = this.el.select('tbody > tr', true).elements;
6649 if(typeof(rows[rowIndex]) == 'undefined'){
6652 rows[rowIndex].dom.style.display = state ? '' : 'none';
6656 getSelectionModel : function(){
6658 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6660 return this.selModel;
6663 * Render the Roo.bootstrap object from renderder
6665 renderCellObject : function(r)
6669 var t = r.cfg.render(r.container);
6672 Roo.each(r.cfg.cn, function(c){
6674 container: t.getChildContainer(),
6677 _this.renderCellObject(child);
6682 getRowIndex : function(row)
6686 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6697 * Returns the grid's underlying element = used by panel.Grid
6698 * @return {Element} The element
6700 getGridEl : function(){
6704 * Forces a resize - used by panel.Grid
6705 * @return {Element} The element
6707 autoSize : function()
6709 //var ctr = Roo.get(this.container.dom.parentElement);
6710 var ctr = Roo.get(this.el.dom);
6712 var thd = this.getGridEl().select('thead',true).first();
6713 var tbd = this.getGridEl().select('tbody', true).first();
6716 var cw = ctr.getWidth();
6720 tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6721 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6724 cw = Math.max(cw, this.totalWidth);
6725 this.getGridEl().select('tr',true).setWidth(cw);
6726 // resize 'expandable coloumn?
6728 return; // we doe not have a view in this design..
6731 onBodyScroll: function()
6734 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6735 this.mainHead.setStyle({
6736 'position' : 'relative',
6737 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6754 * @class Roo.bootstrap.TableCell
6755 * @extends Roo.bootstrap.Component
6756 * Bootstrap TableCell class
6757 * @cfg {String} html cell contain text
6758 * @cfg {String} cls cell class
6759 * @cfg {String} tag cell tag (td|th) default td
6760 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6761 * @cfg {String} align Aligns the content in a cell
6762 * @cfg {String} axis Categorizes cells
6763 * @cfg {String} bgcolor Specifies the background color of a cell
6764 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6765 * @cfg {Number} colspan Specifies the number of columns a cell should span
6766 * @cfg {String} headers Specifies one or more header cells a cell is related to
6767 * @cfg {Number} height Sets the height of a cell
6768 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6769 * @cfg {Number} rowspan Sets the number of rows a cell should span
6770 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6771 * @cfg {String} valign Vertical aligns the content in a cell
6772 * @cfg {Number} width Specifies the width of a cell
6775 * Create a new TableCell
6776 * @param {Object} config The config object
6779 Roo.bootstrap.TableCell = function(config){
6780 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6783 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6803 getAutoCreate : function(){
6804 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6824 cfg.align=this.align
6830 cfg.bgcolor=this.bgcolor
6833 cfg.charoff=this.charoff
6836 cfg.colspan=this.colspan
6839 cfg.headers=this.headers
6842 cfg.height=this.height
6845 cfg.nowrap=this.nowrap
6848 cfg.rowspan=this.rowspan
6851 cfg.scope=this.scope
6854 cfg.valign=this.valign
6857 cfg.width=this.width
6876 * @class Roo.bootstrap.TableRow
6877 * @extends Roo.bootstrap.Component
6878 * Bootstrap TableRow class
6879 * @cfg {String} cls row class
6880 * @cfg {String} align Aligns the content in a table row
6881 * @cfg {String} bgcolor Specifies a background color for a table row
6882 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6883 * @cfg {String} valign Vertical aligns the content in a table row
6886 * Create a new TableRow
6887 * @param {Object} config The config object
6890 Roo.bootstrap.TableRow = function(config){
6891 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6894 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6902 getAutoCreate : function(){
6903 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6913 cfg.align = this.align;
6916 cfg.bgcolor = this.bgcolor;
6919 cfg.charoff = this.charoff;
6922 cfg.valign = this.valign;
6940 * @class Roo.bootstrap.TableBody
6941 * @extends Roo.bootstrap.Component
6942 * Bootstrap TableBody class
6943 * @cfg {String} cls element class
6944 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6945 * @cfg {String} align Aligns the content inside the element
6946 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6947 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6950 * Create a new TableBody
6951 * @param {Object} config The config object
6954 Roo.bootstrap.TableBody = function(config){
6955 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6958 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6966 getAutoCreate : function(){
6967 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6981 cfg.align = this.align;
6984 cfg.charoff = this.charoff;
6987 cfg.valign = this.valign;
6994 // initEvents : function()
7001 // this.store = Roo.factory(this.store, Roo.data);
7002 // this.store.on('load', this.onLoad, this);
7004 // this.store.load();
7008 // onLoad: function ()
7010 // this.fireEvent('load', this);
7020 * Ext JS Library 1.1.1
7021 * Copyright(c) 2006-2007, Ext JS, LLC.
7023 * Originally Released Under LGPL - original licence link has changed is not relivant.
7026 * <script type="text/javascript">
7029 // as we use this in bootstrap.
7030 Roo.namespace('Roo.form');
7032 * @class Roo.form.Action
7033 * Internal Class used to handle form actions
7035 * @param {Roo.form.BasicForm} el The form element or its id
7036 * @param {Object} config Configuration options
7041 // define the action interface
7042 Roo.form.Action = function(form, options){
7044 this.options = options || {};
7047 * Client Validation Failed
7050 Roo.form.Action.CLIENT_INVALID = 'client';
7052 * Server Validation Failed
7055 Roo.form.Action.SERVER_INVALID = 'server';
7057 * Connect to Server Failed
7060 Roo.form.Action.CONNECT_FAILURE = 'connect';
7062 * Reading Data from Server Failed
7065 Roo.form.Action.LOAD_FAILURE = 'load';
7067 Roo.form.Action.prototype = {
7069 failureType : undefined,
7070 response : undefined,
7074 run : function(options){
7079 success : function(response){
7084 handleResponse : function(response){
7088 // default connection failure
7089 failure : function(response){
7091 this.response = response;
7092 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7093 this.form.afterAction(this, false);
7096 processResponse : function(response){
7097 this.response = response;
7098 if(!response.responseText){
7101 this.result = this.handleResponse(response);
7105 // utility functions used internally
7106 getUrl : function(appendParams){
7107 var url = this.options.url || this.form.url || this.form.el.dom.action;
7109 var p = this.getParams();
7111 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7117 getMethod : function(){
7118 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7121 getParams : function(){
7122 var bp = this.form.baseParams;
7123 var p = this.options.params;
7125 if(typeof p == "object"){
7126 p = Roo.urlEncode(Roo.applyIf(p, bp));
7127 }else if(typeof p == 'string' && bp){
7128 p += '&' + Roo.urlEncode(bp);
7131 p = Roo.urlEncode(bp);
7136 createCallback : function(){
7138 success: this.success,
7139 failure: this.failure,
7141 timeout: (this.form.timeout*1000),
7142 upload: this.form.fileUpload ? this.success : undefined
7147 Roo.form.Action.Submit = function(form, options){
7148 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7151 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7154 haveProgress : false,
7155 uploadComplete : false,
7157 // uploadProgress indicator.
7158 uploadProgress : function()
7160 if (!this.form.progressUrl) {
7164 if (!this.haveProgress) {
7165 Roo.MessageBox.progress("Uploading", "Uploading");
7167 if (this.uploadComplete) {
7168 Roo.MessageBox.hide();
7172 this.haveProgress = true;
7174 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7176 var c = new Roo.data.Connection();
7178 url : this.form.progressUrl,
7183 success : function(req){
7184 //console.log(data);
7188 rdata = Roo.decode(req.responseText)
7190 Roo.log("Invalid data from server..");
7194 if (!rdata || !rdata.success) {
7196 Roo.MessageBox.alert(Roo.encode(rdata));
7199 var data = rdata.data;
7201 if (this.uploadComplete) {
7202 Roo.MessageBox.hide();
7207 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7208 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7211 this.uploadProgress.defer(2000,this);
7214 failure: function(data) {
7215 Roo.log('progress url failed ');
7226 // run get Values on the form, so it syncs any secondary forms.
7227 this.form.getValues();
7229 var o = this.options;
7230 var method = this.getMethod();
7231 var isPost = method == 'POST';
7232 if(o.clientValidation === false || this.form.isValid()){
7234 if (this.form.progressUrl) {
7235 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7236 (new Date() * 1) + '' + Math.random());
7241 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7242 form:this.form.el.dom,
7243 url:this.getUrl(!isPost),
7245 params:isPost ? this.getParams() : null,
7246 isUpload: this.form.fileUpload
7249 this.uploadProgress();
7251 }else if (o.clientValidation !== false){ // client validation failed
7252 this.failureType = Roo.form.Action.CLIENT_INVALID;
7253 this.form.afterAction(this, false);
7257 success : function(response)
7259 this.uploadComplete= true;
7260 if (this.haveProgress) {
7261 Roo.MessageBox.hide();
7265 var result = this.processResponse(response);
7266 if(result === true || result.success){
7267 this.form.afterAction(this, true);
7271 this.form.markInvalid(result.errors);
7272 this.failureType = Roo.form.Action.SERVER_INVALID;
7274 this.form.afterAction(this, false);
7276 failure : function(response)
7278 this.uploadComplete= true;
7279 if (this.haveProgress) {
7280 Roo.MessageBox.hide();
7283 this.response = response;
7284 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7285 this.form.afterAction(this, false);
7288 handleResponse : function(response){
7289 if(this.form.errorReader){
7290 var rs = this.form.errorReader.read(response);
7293 for(var i = 0, len = rs.records.length; i < len; i++) {
7294 var r = rs.records[i];
7298 if(errors.length < 1){
7302 success : rs.success,
7308 ret = Roo.decode(response.responseText);
7312 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7322 Roo.form.Action.Load = function(form, options){
7323 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7324 this.reader = this.form.reader;
7327 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7332 Roo.Ajax.request(Roo.apply(
7333 this.createCallback(), {
7334 method:this.getMethod(),
7335 url:this.getUrl(false),
7336 params:this.getParams()
7340 success : function(response){
7342 var result = this.processResponse(response);
7343 if(result === true || !result.success || !result.data){
7344 this.failureType = Roo.form.Action.LOAD_FAILURE;
7345 this.form.afterAction(this, false);
7348 this.form.clearInvalid();
7349 this.form.setValues(result.data);
7350 this.form.afterAction(this, true);
7353 handleResponse : function(response){
7354 if(this.form.reader){
7355 var rs = this.form.reader.read(response);
7356 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7358 success : rs.success,
7362 return Roo.decode(response.responseText);
7366 Roo.form.Action.ACTION_TYPES = {
7367 'load' : Roo.form.Action.Load,
7368 'submit' : Roo.form.Action.Submit
7377 * @class Roo.bootstrap.Form
7378 * @extends Roo.bootstrap.Component
7379 * Bootstrap Form class
7380 * @cfg {String} method GET | POST (default POST)
7381 * @cfg {String} labelAlign top | left (default top)
7382 * @cfg {String} align left | right - for navbars
7383 * @cfg {Boolean} loadMask load mask when submit (default true)
7388 * @param {Object} config The config object
7392 Roo.bootstrap.Form = function(config){
7393 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7396 * @event clientvalidation
7397 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7398 * @param {Form} this
7399 * @param {Boolean} valid true if the form has passed client-side validation
7401 clientvalidation: true,
7403 * @event beforeaction
7404 * Fires before any action is performed. Return false to cancel the action.
7405 * @param {Form} this
7406 * @param {Action} action The action to be performed
7410 * @event actionfailed
7411 * Fires when an action fails.
7412 * @param {Form} this
7413 * @param {Action} action The action that failed
7415 actionfailed : true,
7417 * @event actioncomplete
7418 * Fires when an action is completed.
7419 * @param {Form} this
7420 * @param {Action} action The action that completed
7422 actioncomplete : true
7427 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7430 * @cfg {String} method
7431 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7436 * The URL to use for form actions if one isn't supplied in the action options.
7439 * @cfg {Boolean} fileUpload
7440 * Set to true if this form is a file upload.
7444 * @cfg {Object} baseParams
7445 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7449 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7453 * @cfg {Sting} align (left|right) for navbar forms
7458 activeAction : null,
7461 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7462 * element by passing it or its id or mask the form itself by passing in true.
7465 waitMsgTarget : false,
7469 getAutoCreate : function(){
7473 method : this.method || 'POST',
7474 id : this.id || Roo.id(),
7477 if (this.parent().xtype.match(/^Nav/)) {
7478 cfg.cls = 'navbar-form navbar-' + this.align;
7482 if (this.labelAlign == 'left' ) {
7483 cfg.cls += ' form-horizontal';
7489 initEvents : function()
7491 this.el.on('submit', this.onSubmit, this);
7492 // this was added as random key presses on the form where triggering form submit.
7493 this.el.on('keypress', function(e) {
7494 if (e.getCharCode() != 13) {
7497 // we might need to allow it for textareas.. and some other items.
7498 // check e.getTarget().
7500 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7504 Roo.log("keypress blocked");
7512 onSubmit : function(e){
7517 * Returns true if client-side validation on the form is successful.
7520 isValid : function(){
7521 var items = this.getItems();
7523 items.each(function(f){
7532 * Returns true if any fields in this form have changed since their original load.
7535 isDirty : function(){
7537 var items = this.getItems();
7538 items.each(function(f){
7548 * Performs a predefined action (submit or load) or custom actions you define on this form.
7549 * @param {String} actionName The name of the action type
7550 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7551 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7552 * accept other config options):
7554 Property Type Description
7555 ---------------- --------------- ----------------------------------------------------------------------------------
7556 url String The url for the action (defaults to the form's url)
7557 method String The form method to use (defaults to the form's method, or POST if not defined)
7558 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7559 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7560 validate the form on the client (defaults to false)
7562 * @return {BasicForm} this
7564 doAction : function(action, options){
7565 if(typeof action == 'string'){
7566 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7568 if(this.fireEvent('beforeaction', this, action) !== false){
7569 this.beforeAction(action);
7570 action.run.defer(100, action);
7576 beforeAction : function(action){
7577 var o = action.options;
7580 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7582 // not really supported yet.. ??
7584 //if(this.waitMsgTarget === true){
7585 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7586 //}else if(this.waitMsgTarget){
7587 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7588 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7590 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7596 afterAction : function(action, success){
7597 this.activeAction = null;
7598 var o = action.options;
7600 //if(this.waitMsgTarget === true){
7602 //}else if(this.waitMsgTarget){
7603 // this.waitMsgTarget.unmask();
7605 // Roo.MessageBox.updateProgress(1);
7606 // Roo.MessageBox.hide();
7613 Roo.callback(o.success, o.scope, [this, action]);
7614 this.fireEvent('actioncomplete', this, action);
7618 // failure condition..
7619 // we have a scenario where updates need confirming.
7620 // eg. if a locking scenario exists..
7621 // we look for { errors : { needs_confirm : true }} in the response.
7623 (typeof(action.result) != 'undefined') &&
7624 (typeof(action.result.errors) != 'undefined') &&
7625 (typeof(action.result.errors.needs_confirm) != 'undefined')
7628 Roo.log("not supported yet");
7631 Roo.MessageBox.confirm(
7632 "Change requires confirmation",
7633 action.result.errorMsg,
7638 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7648 Roo.callback(o.failure, o.scope, [this, action]);
7649 // show an error message if no failed handler is set..
7650 if (!this.hasListener('actionfailed')) {
7651 Roo.log("need to add dialog support");
7653 Roo.MessageBox.alert("Error",
7654 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7655 action.result.errorMsg :
7656 "Saving Failed, please check your entries or try again"
7661 this.fireEvent('actionfailed', this, action);
7666 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7667 * @param {String} id The value to search for
7670 findField : function(id){
7671 var items = this.getItems();
7672 var field = items.get(id);
7674 items.each(function(f){
7675 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7682 return field || null;
7685 * Mark fields in this form invalid in bulk.
7686 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7687 * @return {BasicForm} this
7689 markInvalid : function(errors){
7690 if(errors instanceof Array){
7691 for(var i = 0, len = errors.length; i < len; i++){
7692 var fieldError = errors[i];
7693 var f = this.findField(fieldError.id);
7695 f.markInvalid(fieldError.msg);
7701 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7702 field.markInvalid(errors[id]);
7706 //Roo.each(this.childForms || [], function (f) {
7707 // f.markInvalid(errors);
7714 * Set values for fields in this form in bulk.
7715 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7716 * @return {BasicForm} this
7718 setValues : function(values){
7719 if(values instanceof Array){ // array of objects
7720 for(var i = 0, len = values.length; i < len; i++){
7722 var f = this.findField(v.id);
7724 f.setValue(v.value);
7725 if(this.trackResetOnLoad){
7726 f.originalValue = f.getValue();
7730 }else{ // object hash
7733 if(typeof values[id] != 'function' && (field = this.findField(id))){
7735 if (field.setFromData &&
7737 field.displayField &&
7738 // combos' with local stores can
7739 // be queried via setValue()
7740 // to set their value..
7741 (field.store && !field.store.isLocal)
7745 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7746 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7747 field.setFromData(sd);
7750 field.setValue(values[id]);
7754 if(this.trackResetOnLoad){
7755 field.originalValue = field.getValue();
7761 //Roo.each(this.childForms || [], function (f) {
7762 // f.setValues(values);
7769 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7770 * they are returned as an array.
7771 * @param {Boolean} asString
7774 getValues : function(asString){
7775 //if (this.childForms) {
7776 // copy values from the child forms
7777 // Roo.each(this.childForms, function (f) {
7778 // this.setValues(f.getValues());
7784 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7785 if(asString === true){
7788 return Roo.urlDecode(fs);
7792 * Returns the fields in this form as an object with key/value pairs.
7793 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7796 getFieldValues : function(with_hidden)
7798 var items = this.getItems();
7800 items.each(function(f){
7804 var v = f.getValue();
7805 if (f.inputType =='radio') {
7806 if (typeof(ret[f.getName()]) == 'undefined') {
7807 ret[f.getName()] = ''; // empty..
7810 if (!f.el.dom.checked) {
7818 // not sure if this supported any more..
7819 if ((typeof(v) == 'object') && f.getRawValue) {
7820 v = f.getRawValue() ; // dates..
7822 // combo boxes where name != hiddenName...
7823 if (f.name != f.getName()) {
7824 ret[f.name] = f.getRawValue();
7826 ret[f.getName()] = v;
7833 * Clears all invalid messages in this form.
7834 * @return {BasicForm} this
7836 clearInvalid : function(){
7837 var items = this.getItems();
7839 items.each(function(f){
7850 * @return {BasicForm} this
7853 var items = this.getItems();
7854 items.each(function(f){
7858 Roo.each(this.childForms || [], function (f) {
7865 getItems : function()
7867 var r=new Roo.util.MixedCollection(false, function(o){
7868 return o.id || (o.id = Roo.id());
7870 var iter = function(el) {
7877 Roo.each(el.items,function(e) {
7897 * Ext JS Library 1.1.1
7898 * Copyright(c) 2006-2007, Ext JS, LLC.
7900 * Originally Released Under LGPL - original licence link has changed is not relivant.
7903 * <script type="text/javascript">
7906 * @class Roo.form.VTypes
7907 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7910 Roo.form.VTypes = function(){
7911 // closure these in so they are only created once.
7912 var alpha = /^[a-zA-Z_]+$/;
7913 var alphanum = /^[a-zA-Z0-9_]+$/;
7914 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7915 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7917 // All these messages and functions are configurable
7920 * The function used to validate email addresses
7921 * @param {String} value The email address
7923 'email' : function(v){
7924 return email.test(v);
7927 * The error text to display when the email validation function returns false
7930 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7932 * The keystroke filter mask to be applied on email input
7935 'emailMask' : /[a-z0-9_\.\-@]/i,
7938 * The function used to validate URLs
7939 * @param {String} value The URL
7941 'url' : function(v){
7945 * The error text to display when the url validation function returns false
7948 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7951 * The function used to validate alpha values
7952 * @param {String} value The value
7954 'alpha' : function(v){
7955 return alpha.test(v);
7958 * The error text to display when the alpha validation function returns false
7961 'alphaText' : 'This field should only contain letters and _',
7963 * The keystroke filter mask to be applied on alpha input
7966 'alphaMask' : /[a-z_]/i,
7969 * The function used to validate alphanumeric values
7970 * @param {String} value The value
7972 'alphanum' : function(v){
7973 return alphanum.test(v);
7976 * The error text to display when the alphanumeric validation function returns false
7979 'alphanumText' : 'This field should only contain letters, numbers and _',
7981 * The keystroke filter mask to be applied on alphanumeric input
7984 'alphanumMask' : /[a-z0-9_]/i
7994 * @class Roo.bootstrap.Input
7995 * @extends Roo.bootstrap.Component
7996 * Bootstrap Input class
7997 * @cfg {Boolean} disabled is it disabled
7998 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7999 * @cfg {String} name name of the input
8000 * @cfg {string} fieldLabel - the label associated
8001 * @cfg {string} placeholder - placeholder to put in text.
8002 * @cfg {string} before - input group add on before
8003 * @cfg {string} after - input group add on after
8004 * @cfg {string} size - (lg|sm) or leave empty..
8005 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8006 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8007 * @cfg {Number} md colspan out of 12 for computer-sized screens
8008 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8009 * @cfg {string} value default value of the input
8010 * @cfg {Number} labelWidth set the width of label (0-12)
8011 * @cfg {String} labelAlign (top|left)
8012 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8013 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8015 * @cfg {String} align (left|center|right) Default left
8016 * @cfg {Boolean} forceFeedback (true|false) Default false
8022 * Create a new Input
8023 * @param {Object} config The config object
8026 Roo.bootstrap.Input = function(config){
8027 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8032 * Fires when this field receives input focus.
8033 * @param {Roo.form.Field} this
8038 * Fires when this field loses input focus.
8039 * @param {Roo.form.Field} this
8044 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8045 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8046 * @param {Roo.form.Field} this
8047 * @param {Roo.EventObject} e The event object
8052 * Fires just before the field blurs if the field value has changed.
8053 * @param {Roo.form.Field} this
8054 * @param {Mixed} newValue The new value
8055 * @param {Mixed} oldValue The original value
8060 * Fires after the field has been marked as invalid.
8061 * @param {Roo.form.Field} this
8062 * @param {String} msg The validation message
8067 * Fires after the field has been validated with no errors.
8068 * @param {Roo.form.Field} this
8073 * Fires after the key up
8074 * @param {Roo.form.Field} this
8075 * @param {Roo.EventObject} e The event Object
8081 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8083 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8084 automatic validation (defaults to "keyup").
8086 validationEvent : "keyup",
8088 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8090 validateOnBlur : true,
8092 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8094 validationDelay : 250,
8096 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8098 focusClass : "x-form-focus", // not needed???
8102 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8104 invalidClass : "has-warning",
8107 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8109 validClass : "has-success",
8112 * @cfg {Boolean} hasFeedback (true|false) default true
8117 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8119 invalidFeedbackClass : "glyphicon-warning-sign",
8122 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8124 validFeedbackClass : "glyphicon-ok",
8127 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8129 selectOnFocus : false,
8132 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8136 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8141 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8143 disableKeyFilter : false,
8146 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8150 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8154 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8156 blankText : "This field is required",
8159 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8163 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8165 maxLength : Number.MAX_VALUE,
8167 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8169 minLengthText : "The minimum length for this field is {0}",
8171 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8173 maxLengthText : "The maximum length for this field is {0}",
8177 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8178 * If available, this function will be called only after the basic validators all return true, and will be passed the
8179 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8183 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8184 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8185 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8189 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8193 autocomplete: false,
8212 formatedValue : false,
8213 forceFeedback : false,
8215 parentLabelAlign : function()
8218 while (parent.parent()) {
8219 parent = parent.parent();
8220 if (typeof(parent.labelAlign) !='undefined') {
8221 return parent.labelAlign;
8228 getAutoCreate : function(){
8230 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8238 if(this.inputType != 'hidden'){
8239 cfg.cls = 'form-group' //input-group
8245 type : this.inputType,
8247 cls : 'form-control',
8248 placeholder : this.placeholder || '',
8249 autocomplete : this.autocomplete || 'new-password'
8254 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8257 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8258 input.maxLength = this.maxLength;
8261 if (this.disabled) {
8262 input.disabled=true;
8265 if (this.readOnly) {
8266 input.readonly=true;
8270 input.name = this.name;
8273 input.cls += ' input-' + this.size;
8276 ['xs','sm','md','lg'].map(function(size){
8277 if (settings[size]) {
8278 cfg.cls += ' col-' + size + '-' + settings[size];
8282 var inputblock = input;
8286 cls: 'glyphicon form-control-feedback'
8289 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8292 cls : 'has-feedback',
8300 if (this.before || this.after) {
8303 cls : 'input-group',
8307 if (this.before && typeof(this.before) == 'string') {
8309 inputblock.cn.push({
8311 cls : 'roo-input-before input-group-addon',
8315 if (this.before && typeof(this.before) == 'object') {
8316 this.before = Roo.factory(this.before);
8318 inputblock.cn.push({
8320 cls : 'roo-input-before input-group-' +
8321 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8325 inputblock.cn.push(input);
8327 if (this.after && typeof(this.after) == 'string') {
8328 inputblock.cn.push({
8330 cls : 'roo-input-after input-group-addon',
8334 if (this.after && typeof(this.after) == 'object') {
8335 this.after = Roo.factory(this.after);
8337 inputblock.cn.push({
8339 cls : 'roo-input-after input-group-' +
8340 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8344 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8345 inputblock.cls += ' has-feedback';
8346 inputblock.cn.push(feedback);
8350 if (align ==='left' && this.fieldLabel.length) {
8357 cls : 'control-label col-sm-' + this.labelWidth,
8358 html : this.fieldLabel
8362 cls : "col-sm-" + (12 - this.labelWidth),
8369 } else if ( this.fieldLabel.length) {
8375 //cls : 'input-group-addon',
8376 html : this.fieldLabel
8395 if (this.parentType === 'Navbar' && this.parent().bar) {
8396 cfg.cls += ' navbar-form';
8398 if (this.parentType === 'NavGroup') {
8399 cfg.cls += ' navbar-form';
8406 * return the real input element.
8408 inputEl: function ()
8410 return this.el.select('input.form-control',true).first();
8413 tooltipEl : function()
8415 return this.inputEl();
8418 setDisabled : function(v)
8420 var i = this.inputEl().dom;
8422 i.removeAttribute('disabled');
8426 i.setAttribute('disabled','true');
8428 initEvents : function()
8431 this.inputEl().on("keydown" , this.fireKey, this);
8432 this.inputEl().on("focus", this.onFocus, this);
8433 this.inputEl().on("blur", this.onBlur, this);
8435 this.inputEl().relayEvent('keyup', this);
8437 // reference to original value for reset
8438 this.originalValue = this.getValue();
8439 //Roo.form.TextField.superclass.initEvents.call(this);
8440 if(this.validationEvent == 'keyup'){
8441 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8442 this.inputEl().on('keyup', this.filterValidation, this);
8444 else if(this.validationEvent !== false){
8445 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8448 if(this.selectOnFocus){
8449 this.on("focus", this.preFocus, this);
8452 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8453 this.inputEl().on("keypress", this.filterKeys, this);
8456 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8457 this.el.on("click", this.autoSize, this);
8460 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8461 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8464 if (typeof(this.before) == 'object') {
8465 this.before.render(this.el.select('.roo-input-before',true).first());
8467 if (typeof(this.after) == 'object') {
8468 this.after.render(this.el.select('.roo-input-after',true).first());
8473 filterValidation : function(e){
8474 if(!e.isNavKeyPress()){
8475 this.validationTask.delay(this.validationDelay);
8479 * Validates the field value
8480 * @return {Boolean} True if the value is valid, else false
8482 validate : function(){
8483 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8484 if(this.disabled || this.validateValue(this.getRawValue())){
8495 * Validates a value according to the field's validation rules and marks the field as invalid
8496 * if the validation fails
8497 * @param {Mixed} value The value to validate
8498 * @return {Boolean} True if the value is valid, else false
8500 validateValue : function(value){
8501 if(value.length < 1) { // if it's blank
8502 if(this.allowBlank){
8508 if(value.length < this.minLength){
8511 if(value.length > this.maxLength){
8515 var vt = Roo.form.VTypes;
8516 if(!vt[this.vtype](value, this)){
8520 if(typeof this.validator == "function"){
8521 var msg = this.validator(value);
8527 if(this.regex && !this.regex.test(value)){
8537 fireKey : function(e){
8538 //Roo.log('field ' + e.getKey());
8539 if(e.isNavKeyPress()){
8540 this.fireEvent("specialkey", this, e);
8543 focus : function (selectText){
8545 this.inputEl().focus();
8546 if(selectText === true){
8547 this.inputEl().dom.select();
8553 onFocus : function(){
8554 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8555 // this.el.addClass(this.focusClass);
8558 this.hasFocus = true;
8559 this.startValue = this.getValue();
8560 this.fireEvent("focus", this);
8564 beforeBlur : Roo.emptyFn,
8568 onBlur : function(){
8570 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8571 //this.el.removeClass(this.focusClass);
8573 this.hasFocus = false;
8574 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8577 var v = this.getValue();
8578 if(String(v) !== String(this.startValue)){
8579 this.fireEvent('change', this, v, this.startValue);
8581 this.fireEvent("blur", this);
8585 * Resets the current field value to the originally loaded value and clears any validation messages
8588 this.setValue(this.originalValue);
8592 * Returns the name of the field
8593 * @return {Mixed} name The name field
8595 getName: function(){
8599 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8600 * @return {Mixed} value The field value
8602 getValue : function(){
8604 var v = this.inputEl().getValue();
8609 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8610 * @return {Mixed} value The field value
8612 getRawValue : function(){
8613 var v = this.inputEl().getValue();
8619 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8620 * @param {Mixed} value The value to set
8622 setRawValue : function(v){
8623 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8626 selectText : function(start, end){
8627 var v = this.getRawValue();
8629 start = start === undefined ? 0 : start;
8630 end = end === undefined ? v.length : end;
8631 var d = this.inputEl().dom;
8632 if(d.setSelectionRange){
8633 d.setSelectionRange(start, end);
8634 }else if(d.createTextRange){
8635 var range = d.createTextRange();
8636 range.moveStart("character", start);
8637 range.moveEnd("character", v.length-end);
8644 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8645 * @param {Mixed} value The value to set
8647 setValue : function(v){
8650 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8656 processValue : function(value){
8657 if(this.stripCharsRe){
8658 var newValue = value.replace(this.stripCharsRe, '');
8659 if(newValue !== value){
8660 this.setRawValue(newValue);
8667 preFocus : function(){
8669 if(this.selectOnFocus){
8670 this.inputEl().dom.select();
8673 filterKeys : function(e){
8675 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8678 var c = e.getCharCode(), cc = String.fromCharCode(c);
8679 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8682 if(!this.maskRe.test(cc)){
8687 * Clear any invalid styles/messages for this field
8689 clearInvalid : function(){
8691 if(!this.el || this.preventMark){ // not rendered
8695 var label = this.el.select('label', true).first();
8696 var icon = this.el.select('i.fa-star', true).first();
8702 this.el.removeClass(this.invalidClass);
8704 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8706 var feedback = this.el.select('.form-control-feedback', true).first();
8709 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8714 this.fireEvent('valid', this);
8718 * Mark this field as valid
8720 markValid : function()
8722 if(!this.el || this.preventMark){ // not rendered
8726 this.el.removeClass([this.invalidClass, this.validClass]);
8728 var feedback = this.el.select('.form-control-feedback', true).first();
8731 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8734 if(this.disabled || this.allowBlank){
8738 var formGroup = this.el.findParent('.form-group', false, true);
8742 var label = formGroup.select('label', true).first();
8743 var icon = formGroup.select('i.fa-star', true).first();
8750 this.el.addClass(this.validClass);
8752 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8754 var feedback = this.el.select('.form-control-feedback', true).first();
8757 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8758 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8763 this.fireEvent('valid', this);
8767 * Mark this field as invalid
8768 * @param {String} msg The validation message
8770 markInvalid : function(msg)
8772 if(!this.el || this.preventMark){ // not rendered
8776 this.el.removeClass([this.invalidClass, this.validClass]);
8778 var feedback = this.el.select('.form-control-feedback', true).first();
8781 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8784 if(this.disabled || this.allowBlank){
8788 var formGroup = this.el.findParent('.form-group', false, true);
8791 var label = formGroup.select('label', true).first();
8792 var icon = formGroup.select('i.fa-star', true).first();
8794 if(!this.getValue().length && label && !icon){
8795 this.el.findParent('.form-group', false, true).createChild({
8797 cls : 'text-danger fa fa-lg fa-star',
8798 tooltip : 'This field is required',
8799 style : 'margin-right:5px;'
8805 this.el.addClass(this.invalidClass);
8807 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8809 var feedback = this.el.select('.form-control-feedback', true).first();
8812 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8814 if(this.getValue().length || this.forceFeedback){
8815 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8822 this.fireEvent('invalid', this, msg);
8825 SafariOnKeyDown : function(event)
8827 // this is a workaround for a password hang bug on chrome/ webkit.
8829 var isSelectAll = false;
8831 if(this.inputEl().dom.selectionEnd > 0){
8832 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8834 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8835 event.preventDefault();
8840 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8842 event.preventDefault();
8843 // this is very hacky as keydown always get's upper case.
8845 var cc = String.fromCharCode(event.getCharCode());
8846 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8850 adjustWidth : function(tag, w){
8851 tag = tag.toLowerCase();
8852 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8853 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8857 if(tag == 'textarea'){
8860 }else if(Roo.isOpera){
8864 if(tag == 'textarea'){
8883 * @class Roo.bootstrap.TextArea
8884 * @extends Roo.bootstrap.Input
8885 * Bootstrap TextArea class
8886 * @cfg {Number} cols Specifies the visible width of a text area
8887 * @cfg {Number} rows Specifies the visible number of lines in a text area
8888 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8889 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8890 * @cfg {string} html text
8893 * Create a new TextArea
8894 * @param {Object} config The config object
8897 Roo.bootstrap.TextArea = function(config){
8898 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8902 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8912 getAutoCreate : function(){
8914 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8925 value : this.value || '',
8926 html: this.html || '',
8927 cls : 'form-control',
8928 placeholder : this.placeholder || ''
8932 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8933 input.maxLength = this.maxLength;
8937 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8941 input.cols = this.cols;
8944 if (this.readOnly) {
8945 input.readonly = true;
8949 input.name = this.name;
8953 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8957 ['xs','sm','md','lg'].map(function(size){
8958 if (settings[size]) {
8959 cfg.cls += ' col-' + size + '-' + settings[size];
8963 var inputblock = input;
8965 if(this.hasFeedback && !this.allowBlank){
8969 cls: 'glyphicon form-control-feedback'
8973 cls : 'has-feedback',
8982 if (this.before || this.after) {
8985 cls : 'input-group',
8989 inputblock.cn.push({
8991 cls : 'input-group-addon',
8996 inputblock.cn.push(input);
8998 if(this.hasFeedback && !this.allowBlank){
8999 inputblock.cls += ' has-feedback';
9000 inputblock.cn.push(feedback);
9004 inputblock.cn.push({
9006 cls : 'input-group-addon',
9013 if (align ==='left' && this.fieldLabel.length) {
9014 // Roo.log("left and has label");
9020 cls : 'control-label col-sm-' + this.labelWidth,
9021 html : this.fieldLabel
9025 cls : "col-sm-" + (12 - this.labelWidth),
9032 } else if ( this.fieldLabel.length) {
9033 // Roo.log(" label");
9038 //cls : 'input-group-addon',
9039 html : this.fieldLabel
9049 // Roo.log(" no label && no align");
9059 if (this.disabled) {
9060 input.disabled=true;
9067 * return the real textarea element.
9069 inputEl: function ()
9071 return this.el.select('textarea.form-control',true).first();
9075 * Clear any invalid styles/messages for this field
9077 clearInvalid : function()
9080 if(!this.el || this.preventMark){ // not rendered
9084 var label = this.el.select('label', true).first();
9085 var icon = this.el.select('i.fa-star', true).first();
9091 this.el.removeClass(this.invalidClass);
9093 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9095 var feedback = this.el.select('.form-control-feedback', true).first();
9098 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9103 this.fireEvent('valid', this);
9107 * Mark this field as valid
9109 markValid : function()
9111 if(!this.el || this.preventMark){ // not rendered
9115 this.el.removeClass([this.invalidClass, this.validClass]);
9117 var feedback = this.el.select('.form-control-feedback', true).first();
9120 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9123 if(this.disabled || this.allowBlank){
9127 var label = this.el.select('label', true).first();
9128 var icon = this.el.select('i.fa-star', true).first();
9134 this.el.addClass(this.validClass);
9136 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9138 var feedback = this.el.select('.form-control-feedback', true).first();
9141 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9142 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9147 this.fireEvent('valid', this);
9151 * Mark this field as invalid
9152 * @param {String} msg The validation message
9154 markInvalid : function(msg)
9156 if(!this.el || this.preventMark){ // not rendered
9160 this.el.removeClass([this.invalidClass, this.validClass]);
9162 var feedback = this.el.select('.form-control-feedback', true).first();
9165 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9168 if(this.disabled || this.allowBlank){
9172 var label = this.el.select('label', true).first();
9173 var icon = this.el.select('i.fa-star', true).first();
9175 if(!this.getValue().length && label && !icon){
9176 this.el.createChild({
9178 cls : 'text-danger fa fa-lg fa-star',
9179 tooltip : 'This field is required',
9180 style : 'margin-right:5px;'
9184 this.el.addClass(this.invalidClass);
9186 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9188 var feedback = this.el.select('.form-control-feedback', true).first();
9191 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9193 if(this.getValue().length || this.forceFeedback){
9194 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9201 this.fireEvent('invalid', this, msg);
9209 * trigger field - base class for combo..
9214 * @class Roo.bootstrap.TriggerField
9215 * @extends Roo.bootstrap.Input
9216 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9217 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9218 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9219 * for which you can provide a custom implementation. For example:
9221 var trigger = new Roo.bootstrap.TriggerField();
9222 trigger.onTriggerClick = myTriggerFn;
9223 trigger.applyTo('my-field');
9226 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9227 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9228 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9229 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9230 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9233 * Create a new TriggerField.
9234 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9235 * to the base TextField)
9237 Roo.bootstrap.TriggerField = function(config){
9238 this.mimicing = false;
9239 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9242 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9244 * @cfg {String} triggerClass A CSS class to apply to the trigger
9247 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9252 * @cfg {Boolean} removable (true|false) special filter default false
9256 /** @cfg {Boolean} grow @hide */
9257 /** @cfg {Number} growMin @hide */
9258 /** @cfg {Number} growMax @hide */
9264 autoSize: Roo.emptyFn,
9271 actionMode : 'wrap',
9276 getAutoCreate : function(){
9278 var align = this.labelAlign || this.parentLabelAlign();
9283 cls: 'form-group' //input-group
9290 type : this.inputType,
9291 cls : 'form-control',
9292 autocomplete: 'new-password',
9293 placeholder : this.placeholder || ''
9297 input.name = this.name;
9300 input.cls += ' input-' + this.size;
9303 if (this.disabled) {
9304 input.disabled=true;
9307 var inputblock = input;
9309 if(this.hasFeedback && !this.allowBlank){
9313 cls: 'glyphicon form-control-feedback'
9316 if(this.removable && !this.editable && !this.tickable){
9318 cls : 'has-feedback',
9324 cls : 'roo-combo-removable-btn close'
9331 cls : 'has-feedback',
9340 if(this.removable && !this.editable && !this.tickable){
9342 cls : 'roo-removable',
9348 cls : 'roo-combo-removable-btn close'
9355 if (this.before || this.after) {
9358 cls : 'input-group',
9362 inputblock.cn.push({
9364 cls : 'input-group-addon',
9369 inputblock.cn.push(input);
9371 if(this.hasFeedback && !this.allowBlank){
9372 inputblock.cls += ' has-feedback';
9373 inputblock.cn.push(feedback);
9377 inputblock.cn.push({
9379 cls : 'input-group-addon',
9392 cls: 'form-hidden-field'
9406 cls: 'form-hidden-field'
9410 cls: 'roo-select2-choices',
9414 cls: 'roo-select2-search-field',
9427 cls: 'roo-select2-container input-group',
9432 // cls: 'typeahead typeahead-long dropdown-menu',
9433 // style: 'display:none'
9438 if(!this.multiple && this.showToggleBtn){
9444 if (this.caret != false) {
9447 cls: 'fa fa-' + this.caret
9454 cls : 'input-group-addon btn dropdown-toggle',
9459 cls: 'combobox-clear',
9473 combobox.cls += ' roo-select2-container-multi';
9476 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9478 // Roo.log("left and has label");
9484 cls : 'control-label col-sm-' + this.labelWidth,
9485 html : this.fieldLabel
9489 cls : "col-sm-" + (12 - this.labelWidth),
9496 } else if ( this.fieldLabel.length) {
9497 // Roo.log(" label");
9502 //cls : 'input-group-addon',
9503 html : this.fieldLabel
9513 // Roo.log(" no label && no align");
9520 ['xs','sm','md','lg'].map(function(size){
9521 if (settings[size]) {
9522 cfg.cls += ' col-' + size + '-' + settings[size];
9533 onResize : function(w, h){
9534 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9535 // if(typeof w == 'number'){
9536 // var x = w - this.trigger.getWidth();
9537 // this.inputEl().setWidth(this.adjustWidth('input', x));
9538 // this.trigger.setStyle('left', x+'px');
9543 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9546 getResizeEl : function(){
9547 return this.inputEl();
9551 getPositionEl : function(){
9552 return this.inputEl();
9556 alignErrorIcon : function(){
9557 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9561 initEvents : function(){
9565 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9566 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9567 if(!this.multiple && this.showToggleBtn){
9568 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9569 if(this.hideTrigger){
9570 this.trigger.setDisplayed(false);
9572 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9576 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9579 if(this.removable && !this.editable && !this.tickable){
9580 var close = this.closeTriggerEl();
9583 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9584 close.on('click', this.removeBtnClick, this, close);
9588 //this.trigger.addClassOnOver('x-form-trigger-over');
9589 //this.trigger.addClassOnClick('x-form-trigger-click');
9592 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9596 closeTriggerEl : function()
9598 var close = this.el.select('.roo-combo-removable-btn', true).first();
9599 return close ? close : false;
9602 removeBtnClick : function(e, h, el)
9606 if(this.fireEvent("remove", this) !== false){
9608 this.fireEvent("afterremove", this)
9612 createList : function()
9614 this.list = Roo.get(document.body).createChild({
9616 cls: 'typeahead typeahead-long dropdown-menu',
9617 style: 'display:none'
9620 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9625 initTrigger : function(){
9630 onDestroy : function(){
9632 this.trigger.removeAllListeners();
9633 // this.trigger.remove();
9636 // this.wrap.remove();
9638 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9642 onFocus : function(){
9643 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9646 this.wrap.addClass('x-trigger-wrap-focus');
9647 this.mimicing = true;
9648 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9649 if(this.monitorTab){
9650 this.el.on("keydown", this.checkTab, this);
9657 checkTab : function(e){
9658 if(e.getKey() == e.TAB){
9664 onBlur : function(){
9669 mimicBlur : function(e, t){
9671 if(!this.wrap.contains(t) && this.validateBlur()){
9678 triggerBlur : function(){
9679 this.mimicing = false;
9680 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9681 if(this.monitorTab){
9682 this.el.un("keydown", this.checkTab, this);
9684 //this.wrap.removeClass('x-trigger-wrap-focus');
9685 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9689 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9690 validateBlur : function(e, t){
9695 onDisable : function(){
9696 this.inputEl().dom.disabled = true;
9697 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9699 // this.wrap.addClass('x-item-disabled');
9704 onEnable : function(){
9705 this.inputEl().dom.disabled = false;
9706 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9708 // this.el.removeClass('x-item-disabled');
9713 onShow : function(){
9714 var ae = this.getActionEl();
9717 ae.dom.style.display = '';
9718 ae.dom.style.visibility = 'visible';
9724 onHide : function(){
9725 var ae = this.getActionEl();
9726 ae.dom.style.display = 'none';
9730 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9731 * by an implementing function.
9733 * @param {EventObject} e
9735 onTriggerClick : Roo.emptyFn
9739 * Ext JS Library 1.1.1
9740 * Copyright(c) 2006-2007, Ext JS, LLC.
9742 * Originally Released Under LGPL - original licence link has changed is not relivant.
9745 * <script type="text/javascript">
9750 * @class Roo.data.SortTypes
9752 * Defines the default sorting (casting?) comparison functions used when sorting data.
9754 Roo.data.SortTypes = {
9756 * Default sort that does nothing
9757 * @param {Mixed} s The value being converted
9758 * @return {Mixed} The comparison value
9765 * The regular expression used to strip tags
9769 stripTagsRE : /<\/?[^>]+>/gi,
9772 * Strips all HTML tags to sort on text only
9773 * @param {Mixed} s The value being converted
9774 * @return {String} The comparison value
9776 asText : function(s){
9777 return String(s).replace(this.stripTagsRE, "");
9781 * Strips all HTML tags to sort on text only - Case insensitive
9782 * @param {Mixed} s The value being converted
9783 * @return {String} The comparison value
9785 asUCText : function(s){
9786 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9790 * Case insensitive string
9791 * @param {Mixed} s The value being converted
9792 * @return {String} The comparison value
9794 asUCString : function(s) {
9795 return String(s).toUpperCase();
9800 * @param {Mixed} s The value being converted
9801 * @return {Number} The comparison value
9803 asDate : function(s) {
9807 if(s instanceof Date){
9810 return Date.parse(String(s));
9815 * @param {Mixed} s The value being converted
9816 * @return {Float} The comparison value
9818 asFloat : function(s) {
9819 var val = parseFloat(String(s).replace(/,/g, ""));
9828 * @param {Mixed} s The value being converted
9829 * @return {Number} The comparison value
9831 asInt : function(s) {
9832 var val = parseInt(String(s).replace(/,/g, ""));
9840 * Ext JS Library 1.1.1
9841 * Copyright(c) 2006-2007, Ext JS, LLC.
9843 * Originally Released Under LGPL - original licence link has changed is not relivant.
9846 * <script type="text/javascript">
9850 * @class Roo.data.Record
9851 * Instances of this class encapsulate both record <em>definition</em> information, and record
9852 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9853 * to access Records cached in an {@link Roo.data.Store} object.<br>
9855 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9856 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9859 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9861 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9862 * {@link #create}. The parameters are the same.
9863 * @param {Array} data An associative Array of data values keyed by the field name.
9864 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9865 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9866 * not specified an integer id is generated.
9868 Roo.data.Record = function(data, id){
9869 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9874 * Generate a constructor for a specific record layout.
9875 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9876 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9877 * Each field definition object may contain the following properties: <ul>
9878 * <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,
9879 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9880 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9881 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9882 * is being used, then this is a string containing the javascript expression to reference the data relative to
9883 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9884 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9885 * this may be omitted.</p></li>
9886 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9887 * <ul><li>auto (Default, implies no conversion)</li>
9892 * <li>date</li></ul></p></li>
9893 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9894 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9895 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9896 * by the Reader into an object that will be stored in the Record. It is passed the
9897 * following parameters:<ul>
9898 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9900 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9902 * <br>usage:<br><pre><code>
9903 var TopicRecord = Roo.data.Record.create(
9904 {name: 'title', mapping: 'topic_title'},
9905 {name: 'author', mapping: 'username'},
9906 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9907 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9908 {name: 'lastPoster', mapping: 'user2'},
9909 {name: 'excerpt', mapping: 'post_text'}
9912 var myNewRecord = new TopicRecord({
9913 title: 'Do my job please',
9916 lastPost: new Date(),
9917 lastPoster: 'Animal',
9918 excerpt: 'No way dude!'
9920 myStore.add(myNewRecord);
9925 Roo.data.Record.create = function(o){
9927 f.superclass.constructor.apply(this, arguments);
9929 Roo.extend(f, Roo.data.Record);
9930 var p = f.prototype;
9931 p.fields = new Roo.util.MixedCollection(false, function(field){
9934 for(var i = 0, len = o.length; i < len; i++){
9935 p.fields.add(new Roo.data.Field(o[i]));
9937 f.getField = function(name){
9938 return p.fields.get(name);
9943 Roo.data.Record.AUTO_ID = 1000;
9944 Roo.data.Record.EDIT = 'edit';
9945 Roo.data.Record.REJECT = 'reject';
9946 Roo.data.Record.COMMIT = 'commit';
9948 Roo.data.Record.prototype = {
9950 * Readonly flag - true if this record has been modified.
9959 join : function(store){
9964 * Set the named field to the specified value.
9965 * @param {String} name The name of the field to set.
9966 * @param {Object} value The value to set the field to.
9968 set : function(name, value){
9969 if(this.data[name] == value){
9976 if(typeof this.modified[name] == 'undefined'){
9977 this.modified[name] = this.data[name];
9979 this.data[name] = value;
9980 if(!this.editing && this.store){
9981 this.store.afterEdit(this);
9986 * Get the value of the named field.
9987 * @param {String} name The name of the field to get the value of.
9988 * @return {Object} The value of the field.
9990 get : function(name){
9991 return this.data[name];
9995 beginEdit : function(){
9996 this.editing = true;
10001 cancelEdit : function(){
10002 this.editing = false;
10003 delete this.modified;
10007 endEdit : function(){
10008 this.editing = false;
10009 if(this.dirty && this.store){
10010 this.store.afterEdit(this);
10015 * Usually called by the {@link Roo.data.Store} which owns the Record.
10016 * Rejects all changes made to the Record since either creation, or the last commit operation.
10017 * Modified fields are reverted to their original values.
10019 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10020 * of reject operations.
10022 reject : function(){
10023 var m = this.modified;
10025 if(typeof m[n] != "function"){
10026 this.data[n] = m[n];
10029 this.dirty = false;
10030 delete this.modified;
10031 this.editing = false;
10033 this.store.afterReject(this);
10038 * Usually called by the {@link Roo.data.Store} which owns the Record.
10039 * Commits all changes made to the Record since either creation, or the last commit operation.
10041 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10042 * of commit operations.
10044 commit : function(){
10045 this.dirty = false;
10046 delete this.modified;
10047 this.editing = false;
10049 this.store.afterCommit(this);
10054 hasError : function(){
10055 return this.error != null;
10059 clearError : function(){
10064 * Creates a copy of this record.
10065 * @param {String} id (optional) A new record id if you don't want to use this record's id
10068 copy : function(newId) {
10069 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10073 * Ext JS Library 1.1.1
10074 * Copyright(c) 2006-2007, Ext JS, LLC.
10076 * Originally Released Under LGPL - original licence link has changed is not relivant.
10079 * <script type="text/javascript">
10085 * @class Roo.data.Store
10086 * @extends Roo.util.Observable
10087 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10088 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10090 * 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
10091 * has no knowledge of the format of the data returned by the Proxy.<br>
10093 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10094 * instances from the data object. These records are cached and made available through accessor functions.
10096 * Creates a new Store.
10097 * @param {Object} config A config object containing the objects needed for the Store to access data,
10098 * and read the data into Records.
10100 Roo.data.Store = function(config){
10101 this.data = new Roo.util.MixedCollection(false);
10102 this.data.getKey = function(o){
10105 this.baseParams = {};
10107 this.paramNames = {
10112 "multisort" : "_multisort"
10115 if(config && config.data){
10116 this.inlineData = config.data;
10117 delete config.data;
10120 Roo.apply(this, config);
10122 if(this.reader){ // reader passed
10123 this.reader = Roo.factory(this.reader, Roo.data);
10124 this.reader.xmodule = this.xmodule || false;
10125 if(!this.recordType){
10126 this.recordType = this.reader.recordType;
10128 if(this.reader.onMetaChange){
10129 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10133 if(this.recordType){
10134 this.fields = this.recordType.prototype.fields;
10136 this.modified = [];
10140 * @event datachanged
10141 * Fires when the data cache has changed, and a widget which is using this Store
10142 * as a Record cache should refresh its view.
10143 * @param {Store} this
10145 datachanged : true,
10147 * @event metachange
10148 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10149 * @param {Store} this
10150 * @param {Object} meta The JSON metadata
10155 * Fires when Records have been added to the Store
10156 * @param {Store} this
10157 * @param {Roo.data.Record[]} records The array of Records added
10158 * @param {Number} index The index at which the record(s) were added
10163 * Fires when a Record has been removed from the Store
10164 * @param {Store} this
10165 * @param {Roo.data.Record} record The Record that was removed
10166 * @param {Number} index The index at which the record was removed
10171 * Fires when a Record has been updated
10172 * @param {Store} this
10173 * @param {Roo.data.Record} record The Record that was updated
10174 * @param {String} operation The update operation being performed. Value may be one of:
10176 Roo.data.Record.EDIT
10177 Roo.data.Record.REJECT
10178 Roo.data.Record.COMMIT
10184 * Fires when the data cache has been cleared.
10185 * @param {Store} this
10189 * @event beforeload
10190 * Fires before a request is made for a new data object. If the beforeload handler returns false
10191 * the load action will be canceled.
10192 * @param {Store} this
10193 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10197 * @event beforeloadadd
10198 * Fires after a new set of Records has been loaded.
10199 * @param {Store} this
10200 * @param {Roo.data.Record[]} records The Records that were loaded
10201 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10203 beforeloadadd : true,
10206 * Fires after a new set of Records has been loaded, before they are added to the store.
10207 * @param {Store} this
10208 * @param {Roo.data.Record[]} records The Records that were loaded
10209 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10210 * @params {Object} return from reader
10214 * @event loadexception
10215 * Fires if an exception occurs in the Proxy during loading.
10216 * Called with the signature of the Proxy's "loadexception" event.
10217 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10220 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10221 * @param {Object} load options
10222 * @param {Object} jsonData from your request (normally this contains the Exception)
10224 loadexception : true
10228 this.proxy = Roo.factory(this.proxy, Roo.data);
10229 this.proxy.xmodule = this.xmodule || false;
10230 this.relayEvents(this.proxy, ["loadexception"]);
10232 this.sortToggle = {};
10233 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10235 Roo.data.Store.superclass.constructor.call(this);
10237 if(this.inlineData){
10238 this.loadData(this.inlineData);
10239 delete this.inlineData;
10243 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10245 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10246 * without a remote query - used by combo/forms at present.
10250 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10253 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10256 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10257 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10260 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10261 * on any HTTP request
10264 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10267 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10271 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10272 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10274 remoteSort : false,
10277 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10278 * loaded or when a record is removed. (defaults to false).
10280 pruneModifiedRecords : false,
10283 lastOptions : null,
10286 * Add Records to the Store and fires the add event.
10287 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10289 add : function(records){
10290 records = [].concat(records);
10291 for(var i = 0, len = records.length; i < len; i++){
10292 records[i].join(this);
10294 var index = this.data.length;
10295 this.data.addAll(records);
10296 this.fireEvent("add", this, records, index);
10300 * Remove a Record from the Store and fires the remove event.
10301 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10303 remove : function(record){
10304 var index = this.data.indexOf(record);
10305 this.data.removeAt(index);
10306 if(this.pruneModifiedRecords){
10307 this.modified.remove(record);
10309 this.fireEvent("remove", this, record, index);
10313 * Remove all Records from the Store and fires the clear event.
10315 removeAll : function(){
10317 if(this.pruneModifiedRecords){
10318 this.modified = [];
10320 this.fireEvent("clear", this);
10324 * Inserts Records to the Store at the given index and fires the add event.
10325 * @param {Number} index The start index at which to insert the passed Records.
10326 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10328 insert : function(index, records){
10329 records = [].concat(records);
10330 for(var i = 0, len = records.length; i < len; i++){
10331 this.data.insert(index, records[i]);
10332 records[i].join(this);
10334 this.fireEvent("add", this, records, index);
10338 * Get the index within the cache of the passed Record.
10339 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10340 * @return {Number} The index of the passed Record. Returns -1 if not found.
10342 indexOf : function(record){
10343 return this.data.indexOf(record);
10347 * Get the index within the cache of the Record with the passed id.
10348 * @param {String} id The id of the Record to find.
10349 * @return {Number} The index of the Record. Returns -1 if not found.
10351 indexOfId : function(id){
10352 return this.data.indexOfKey(id);
10356 * Get the Record with the specified id.
10357 * @param {String} id The id of the Record to find.
10358 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10360 getById : function(id){
10361 return this.data.key(id);
10365 * Get the Record at the specified index.
10366 * @param {Number} index The index of the Record to find.
10367 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10369 getAt : function(index){
10370 return this.data.itemAt(index);
10374 * Returns a range of Records between specified indices.
10375 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10376 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10377 * @return {Roo.data.Record[]} An array of Records
10379 getRange : function(start, end){
10380 return this.data.getRange(start, end);
10384 storeOptions : function(o){
10385 o = Roo.apply({}, o);
10388 this.lastOptions = o;
10392 * Loads the Record cache from the configured Proxy using the configured Reader.
10394 * If using remote paging, then the first load call must specify the <em>start</em>
10395 * and <em>limit</em> properties in the options.params property to establish the initial
10396 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10398 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10399 * and this call will return before the new data has been loaded. Perform any post-processing
10400 * in a callback function, or in a "load" event handler.</strong>
10402 * @param {Object} options An object containing properties which control loading options:<ul>
10403 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10404 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10405 * passed the following arguments:<ul>
10406 * <li>r : Roo.data.Record[]</li>
10407 * <li>options: Options object from the load call</li>
10408 * <li>success: Boolean success indicator</li></ul></li>
10409 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10410 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10413 load : function(options){
10414 options = options || {};
10415 if(this.fireEvent("beforeload", this, options) !== false){
10416 this.storeOptions(options);
10417 var p = Roo.apply(options.params || {}, this.baseParams);
10418 // if meta was not loaded from remote source.. try requesting it.
10419 if (!this.reader.metaFromRemote) {
10420 p._requestMeta = 1;
10422 if(this.sortInfo && this.remoteSort){
10423 var pn = this.paramNames;
10424 p[pn["sort"]] = this.sortInfo.field;
10425 p[pn["dir"]] = this.sortInfo.direction;
10427 if (this.multiSort) {
10428 var pn = this.paramNames;
10429 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10432 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10437 * Reloads the Record cache from the configured Proxy using the configured Reader and
10438 * the options from the last load operation performed.
10439 * @param {Object} options (optional) An object containing properties which may override the options
10440 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10441 * the most recently used options are reused).
10443 reload : function(options){
10444 this.load(Roo.applyIf(options||{}, this.lastOptions));
10448 // Called as a callback by the Reader during a load operation.
10449 loadRecords : function(o, options, success){
10450 if(!o || success === false){
10451 if(success !== false){
10452 this.fireEvent("load", this, [], options, o);
10454 if(options.callback){
10455 options.callback.call(options.scope || this, [], options, false);
10459 // if data returned failure - throw an exception.
10460 if (o.success === false) {
10461 // show a message if no listener is registered.
10462 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10463 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10465 // loadmask wil be hooked into this..
10466 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10469 var r = o.records, t = o.totalRecords || r.length;
10471 this.fireEvent("beforeloadadd", this, r, options, o);
10473 if(!options || options.add !== true){
10474 if(this.pruneModifiedRecords){
10475 this.modified = [];
10477 for(var i = 0, len = r.length; i < len; i++){
10481 this.data = this.snapshot;
10482 delete this.snapshot;
10485 this.data.addAll(r);
10486 this.totalLength = t;
10488 this.fireEvent("datachanged", this);
10490 this.totalLength = Math.max(t, this.data.length+r.length);
10493 this.fireEvent("load", this, r, options, o);
10494 if(options.callback){
10495 options.callback.call(options.scope || this, r, options, true);
10501 * Loads data from a passed data block. A Reader which understands the format of the data
10502 * must have been configured in the constructor.
10503 * @param {Object} data The data block from which to read the Records. The format of the data expected
10504 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10505 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10507 loadData : function(o, append){
10508 var r = this.reader.readRecords(o);
10509 this.loadRecords(r, {add: append}, true);
10513 * Gets the number of cached records.
10515 * <em>If using paging, this may not be the total size of the dataset. If the data object
10516 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10517 * the data set size</em>
10519 getCount : function(){
10520 return this.data.length || 0;
10524 * Gets the total number of records in the dataset as returned by the server.
10526 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10527 * the dataset size</em>
10529 getTotalCount : function(){
10530 return this.totalLength || 0;
10534 * Returns the sort state of the Store as an object with two properties:
10536 field {String} The name of the field by which the Records are sorted
10537 direction {String} The sort order, "ASC" or "DESC"
10540 getSortState : function(){
10541 return this.sortInfo;
10545 applySort : function(){
10546 if(this.sortInfo && !this.remoteSort){
10547 var s = this.sortInfo, f = s.field;
10548 var st = this.fields.get(f).sortType;
10549 var fn = function(r1, r2){
10550 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10551 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10553 this.data.sort(s.direction, fn);
10554 if(this.snapshot && this.snapshot != this.data){
10555 this.snapshot.sort(s.direction, fn);
10561 * Sets the default sort column and order to be used by the next load operation.
10562 * @param {String} fieldName The name of the field to sort by.
10563 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10565 setDefaultSort : function(field, dir){
10566 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10570 * Sort the Records.
10571 * If remote sorting is used, the sort is performed on the server, and the cache is
10572 * reloaded. If local sorting is used, the cache is sorted internally.
10573 * @param {String} fieldName The name of the field to sort by.
10574 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10576 sort : function(fieldName, dir){
10577 var f = this.fields.get(fieldName);
10579 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10581 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10582 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10587 this.sortToggle[f.name] = dir;
10588 this.sortInfo = {field: f.name, direction: dir};
10589 if(!this.remoteSort){
10591 this.fireEvent("datachanged", this);
10593 this.load(this.lastOptions);
10598 * Calls the specified function for each of the Records in the cache.
10599 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10600 * Returning <em>false</em> aborts and exits the iteration.
10601 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10603 each : function(fn, scope){
10604 this.data.each(fn, scope);
10608 * Gets all records modified since the last commit. Modified records are persisted across load operations
10609 * (e.g., during paging).
10610 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10612 getModifiedRecords : function(){
10613 return this.modified;
10617 createFilterFn : function(property, value, anyMatch){
10618 if(!value.exec){ // not a regex
10619 value = String(value);
10620 if(value.length == 0){
10623 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10625 return function(r){
10626 return value.test(r.data[property]);
10631 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10632 * @param {String} property A field on your records
10633 * @param {Number} start The record index to start at (defaults to 0)
10634 * @param {Number} end The last record index to include (defaults to length - 1)
10635 * @return {Number} The sum
10637 sum : function(property, start, end){
10638 var rs = this.data.items, v = 0;
10639 start = start || 0;
10640 end = (end || end === 0) ? end : rs.length-1;
10642 for(var i = start; i <= end; i++){
10643 v += (rs[i].data[property] || 0);
10649 * Filter the records by a specified property.
10650 * @param {String} field A field on your records
10651 * @param {String/RegExp} value Either a string that the field
10652 * should start with or a RegExp to test against the field
10653 * @param {Boolean} anyMatch True to match any part not just the beginning
10655 filter : function(property, value, anyMatch){
10656 var fn = this.createFilterFn(property, value, anyMatch);
10657 return fn ? this.filterBy(fn) : this.clearFilter();
10661 * Filter by a function. The specified function will be called with each
10662 * record in this data source. If the function returns true the record is included,
10663 * otherwise it is filtered.
10664 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10665 * @param {Object} scope (optional) The scope of the function (defaults to this)
10667 filterBy : function(fn, scope){
10668 this.snapshot = this.snapshot || this.data;
10669 this.data = this.queryBy(fn, scope||this);
10670 this.fireEvent("datachanged", this);
10674 * Query the records by a specified property.
10675 * @param {String} field A field on your records
10676 * @param {String/RegExp} value Either a string that the field
10677 * should start with or a RegExp to test against the field
10678 * @param {Boolean} anyMatch True to match any part not just the beginning
10679 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10681 query : function(property, value, anyMatch){
10682 var fn = this.createFilterFn(property, value, anyMatch);
10683 return fn ? this.queryBy(fn) : this.data.clone();
10687 * Query by a function. The specified function will be called with each
10688 * record in this data source. If the function returns true the record is included
10690 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10691 * @param {Object} scope (optional) The scope of the function (defaults to this)
10692 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10694 queryBy : function(fn, scope){
10695 var data = this.snapshot || this.data;
10696 return data.filterBy(fn, scope||this);
10700 * Collects unique values for a particular dataIndex from this store.
10701 * @param {String} dataIndex The property to collect
10702 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10703 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10704 * @return {Array} An array of the unique values
10706 collect : function(dataIndex, allowNull, bypassFilter){
10707 var d = (bypassFilter === true && this.snapshot) ?
10708 this.snapshot.items : this.data.items;
10709 var v, sv, r = [], l = {};
10710 for(var i = 0, len = d.length; i < len; i++){
10711 v = d[i].data[dataIndex];
10713 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10722 * Revert to a view of the Record cache with no filtering applied.
10723 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10725 clearFilter : function(suppressEvent){
10726 if(this.snapshot && this.snapshot != this.data){
10727 this.data = this.snapshot;
10728 delete this.snapshot;
10729 if(suppressEvent !== true){
10730 this.fireEvent("datachanged", this);
10736 afterEdit : function(record){
10737 if(this.modified.indexOf(record) == -1){
10738 this.modified.push(record);
10740 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10744 afterReject : function(record){
10745 this.modified.remove(record);
10746 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10750 afterCommit : function(record){
10751 this.modified.remove(record);
10752 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10756 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10757 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10759 commitChanges : function(){
10760 var m = this.modified.slice(0);
10761 this.modified = [];
10762 for(var i = 0, len = m.length; i < len; i++){
10768 * Cancel outstanding changes on all changed records.
10770 rejectChanges : function(){
10771 var m = this.modified.slice(0);
10772 this.modified = [];
10773 for(var i = 0, len = m.length; i < len; i++){
10778 onMetaChange : function(meta, rtype, o){
10779 this.recordType = rtype;
10780 this.fields = rtype.prototype.fields;
10781 delete this.snapshot;
10782 this.sortInfo = meta.sortInfo || this.sortInfo;
10783 this.modified = [];
10784 this.fireEvent('metachange', this, this.reader.meta);
10787 moveIndex : function(data, type)
10789 var index = this.indexOf(data);
10791 var newIndex = index + type;
10795 this.insert(newIndex, data);
10800 * Ext JS Library 1.1.1
10801 * Copyright(c) 2006-2007, Ext JS, LLC.
10803 * Originally Released Under LGPL - original licence link has changed is not relivant.
10806 * <script type="text/javascript">
10810 * @class Roo.data.SimpleStore
10811 * @extends Roo.data.Store
10812 * Small helper class to make creating Stores from Array data easier.
10813 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10814 * @cfg {Array} fields An array of field definition objects, or field name strings.
10815 * @cfg {Array} data The multi-dimensional array of data
10817 * @param {Object} config
10819 Roo.data.SimpleStore = function(config){
10820 Roo.data.SimpleStore.superclass.constructor.call(this, {
10822 reader: new Roo.data.ArrayReader({
10825 Roo.data.Record.create(config.fields)
10827 proxy : new Roo.data.MemoryProxy(config.data)
10831 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10833 * Ext JS Library 1.1.1
10834 * Copyright(c) 2006-2007, Ext JS, LLC.
10836 * Originally Released Under LGPL - original licence link has changed is not relivant.
10839 * <script type="text/javascript">
10844 * @extends Roo.data.Store
10845 * @class Roo.data.JsonStore
10846 * Small helper class to make creating Stores for JSON data easier. <br/>
10848 var store = new Roo.data.JsonStore({
10849 url: 'get-images.php',
10851 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10854 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10855 * JsonReader and HttpProxy (unless inline data is provided).</b>
10856 * @cfg {Array} fields An array of field definition objects, or field name strings.
10858 * @param {Object} config
10860 Roo.data.JsonStore = function(c){
10861 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10862 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10863 reader: new Roo.data.JsonReader(c, c.fields)
10866 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10868 * Ext JS Library 1.1.1
10869 * Copyright(c) 2006-2007, Ext JS, LLC.
10871 * Originally Released Under LGPL - original licence link has changed is not relivant.
10874 * <script type="text/javascript">
10878 Roo.data.Field = function(config){
10879 if(typeof config == "string"){
10880 config = {name: config};
10882 Roo.apply(this, config);
10885 this.type = "auto";
10888 var st = Roo.data.SortTypes;
10889 // named sortTypes are supported, here we look them up
10890 if(typeof this.sortType == "string"){
10891 this.sortType = st[this.sortType];
10894 // set default sortType for strings and dates
10895 if(!this.sortType){
10898 this.sortType = st.asUCString;
10901 this.sortType = st.asDate;
10904 this.sortType = st.none;
10909 var stripRe = /[\$,%]/g;
10911 // prebuilt conversion function for this field, instead of
10912 // switching every time we're reading a value
10914 var cv, dateFormat = this.dateFormat;
10919 cv = function(v){ return v; };
10922 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10926 return v !== undefined && v !== null && v !== '' ?
10927 parseInt(String(v).replace(stripRe, ""), 10) : '';
10932 return v !== undefined && v !== null && v !== '' ?
10933 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10938 cv = function(v){ return v === true || v === "true" || v == 1; };
10945 if(v instanceof Date){
10949 if(dateFormat == "timestamp"){
10950 return new Date(v*1000);
10952 return Date.parseDate(v, dateFormat);
10954 var parsed = Date.parse(v);
10955 return parsed ? new Date(parsed) : null;
10964 Roo.data.Field.prototype = {
10972 * Ext JS Library 1.1.1
10973 * Copyright(c) 2006-2007, Ext JS, LLC.
10975 * Originally Released Under LGPL - original licence link has changed is not relivant.
10978 * <script type="text/javascript">
10981 // Base class for reading structured data from a data source. This class is intended to be
10982 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10985 * @class Roo.data.DataReader
10986 * Base class for reading structured data from a data source. This class is intended to be
10987 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10990 Roo.data.DataReader = function(meta, recordType){
10994 this.recordType = recordType instanceof Array ?
10995 Roo.data.Record.create(recordType) : recordType;
10998 Roo.data.DataReader.prototype = {
11000 * Create an empty record
11001 * @param {Object} data (optional) - overlay some values
11002 * @return {Roo.data.Record} record created.
11004 newRow : function(d) {
11006 this.recordType.prototype.fields.each(function(c) {
11008 case 'int' : da[c.name] = 0; break;
11009 case 'date' : da[c.name] = new Date(); break;
11010 case 'float' : da[c.name] = 0.0; break;
11011 case 'boolean' : da[c.name] = false; break;
11012 default : da[c.name] = ""; break;
11016 return new this.recordType(Roo.apply(da, d));
11021 * Ext JS Library 1.1.1
11022 * Copyright(c) 2006-2007, Ext JS, LLC.
11024 * Originally Released Under LGPL - original licence link has changed is not relivant.
11027 * <script type="text/javascript">
11031 * @class Roo.data.DataProxy
11032 * @extends Roo.data.Observable
11033 * This class is an abstract base class for implementations which provide retrieval of
11034 * unformatted data objects.<br>
11036 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11037 * (of the appropriate type which knows how to parse the data object) to provide a block of
11038 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11040 * Custom implementations must implement the load method as described in
11041 * {@link Roo.data.HttpProxy#load}.
11043 Roo.data.DataProxy = function(){
11046 * @event beforeload
11047 * Fires before a network request is made to retrieve a data object.
11048 * @param {Object} This DataProxy object.
11049 * @param {Object} params The params parameter to the load function.
11054 * Fires before the load method's callback is called.
11055 * @param {Object} This DataProxy object.
11056 * @param {Object} o The data object.
11057 * @param {Object} arg The callback argument object passed to the load function.
11061 * @event loadexception
11062 * Fires if an Exception occurs during data retrieval.
11063 * @param {Object} This DataProxy object.
11064 * @param {Object} o The data object.
11065 * @param {Object} arg The callback argument object passed to the load function.
11066 * @param {Object} e The Exception.
11068 loadexception : true
11070 Roo.data.DataProxy.superclass.constructor.call(this);
11073 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11076 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11080 * Ext JS Library 1.1.1
11081 * Copyright(c) 2006-2007, Ext JS, LLC.
11083 * Originally Released Under LGPL - original licence link has changed is not relivant.
11086 * <script type="text/javascript">
11089 * @class Roo.data.MemoryProxy
11090 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11091 * to the Reader when its load method is called.
11093 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11095 Roo.data.MemoryProxy = function(data){
11099 Roo.data.MemoryProxy.superclass.constructor.call(this);
11103 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11106 * Load data from the requested source (in this case an in-memory
11107 * data object passed to the constructor), read the data object into
11108 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11109 * process that block using the passed callback.
11110 * @param {Object} params This parameter is not used by the MemoryProxy class.
11111 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11112 * object into a block of Roo.data.Records.
11113 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11114 * The function must be passed <ul>
11115 * <li>The Record block object</li>
11116 * <li>The "arg" argument from the load function</li>
11117 * <li>A boolean success indicator</li>
11119 * @param {Object} scope The scope in which to call the callback
11120 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11122 load : function(params, reader, callback, scope, arg){
11123 params = params || {};
11126 result = reader.readRecords(this.data);
11128 this.fireEvent("loadexception", this, arg, null, e);
11129 callback.call(scope, null, arg, false);
11132 callback.call(scope, result, arg, true);
11136 update : function(params, records){
11141 * Ext JS Library 1.1.1
11142 * Copyright(c) 2006-2007, Ext JS, LLC.
11144 * Originally Released Under LGPL - original licence link has changed is not relivant.
11147 * <script type="text/javascript">
11150 * @class Roo.data.HttpProxy
11151 * @extends Roo.data.DataProxy
11152 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11153 * configured to reference a certain URL.<br><br>
11155 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11156 * from which the running page was served.<br><br>
11158 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11160 * Be aware that to enable the browser to parse an XML document, the server must set
11161 * the Content-Type header in the HTTP response to "text/xml".
11163 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11164 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11165 * will be used to make the request.
11167 Roo.data.HttpProxy = function(conn){
11168 Roo.data.HttpProxy.superclass.constructor.call(this);
11169 // is conn a conn config or a real conn?
11171 this.useAjax = !conn || !conn.events;
11175 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11176 // thse are take from connection...
11179 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11182 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11183 * extra parameters to each request made by this object. (defaults to undefined)
11186 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11187 * to each request made by this object. (defaults to undefined)
11190 * @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)
11193 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11196 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11202 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11206 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11207 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11208 * a finer-grained basis than the DataProxy events.
11210 getConnection : function(){
11211 return this.useAjax ? Roo.Ajax : this.conn;
11215 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11216 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11217 * process that block using the passed callback.
11218 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11219 * for the request to the remote server.
11220 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11221 * object into a block of Roo.data.Records.
11222 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11223 * The function must be passed <ul>
11224 * <li>The Record block object</li>
11225 * <li>The "arg" argument from the load function</li>
11226 * <li>A boolean success indicator</li>
11228 * @param {Object} scope The scope in which to call the callback
11229 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11231 load : function(params, reader, callback, scope, arg){
11232 if(this.fireEvent("beforeload", this, params) !== false){
11234 params : params || {},
11236 callback : callback,
11241 callback : this.loadResponse,
11245 Roo.applyIf(o, this.conn);
11246 if(this.activeRequest){
11247 Roo.Ajax.abort(this.activeRequest);
11249 this.activeRequest = Roo.Ajax.request(o);
11251 this.conn.request(o);
11254 callback.call(scope||this, null, arg, false);
11259 loadResponse : function(o, success, response){
11260 delete this.activeRequest;
11262 this.fireEvent("loadexception", this, o, response);
11263 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11268 result = o.reader.read(response);
11270 this.fireEvent("loadexception", this, o, response, e);
11271 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11275 this.fireEvent("load", this, o, o.request.arg);
11276 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11280 update : function(dataSet){
11285 updateResponse : function(dataSet){
11290 * Ext JS Library 1.1.1
11291 * Copyright(c) 2006-2007, Ext JS, LLC.
11293 * Originally Released Under LGPL - original licence link has changed is not relivant.
11296 * <script type="text/javascript">
11300 * @class Roo.data.ScriptTagProxy
11301 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11302 * other than the originating domain of the running page.<br><br>
11304 * <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
11305 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11307 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11308 * source code that is used as the source inside a <script> tag.<br><br>
11310 * In order for the browser to process the returned data, the server must wrap the data object
11311 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11312 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11313 * depending on whether the callback name was passed:
11316 boolean scriptTag = false;
11317 String cb = request.getParameter("callback");
11320 response.setContentType("text/javascript");
11322 response.setContentType("application/x-json");
11324 Writer out = response.getWriter();
11326 out.write(cb + "(");
11328 out.print(dataBlock.toJsonString());
11335 * @param {Object} config A configuration object.
11337 Roo.data.ScriptTagProxy = function(config){
11338 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11339 Roo.apply(this, config);
11340 this.head = document.getElementsByTagName("head")[0];
11343 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11345 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11347 * @cfg {String} url The URL from which to request the data object.
11350 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11354 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11355 * the server the name of the callback function set up by the load call to process the returned data object.
11356 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11357 * javascript output which calls this named function passing the data object as its only parameter.
11359 callbackParam : "callback",
11361 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11362 * name to the request.
11367 * Load data from the configured URL, read the data object into
11368 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11369 * process that block using the passed callback.
11370 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11371 * for the request to the remote server.
11372 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11373 * object into a block of Roo.data.Records.
11374 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11375 * The function must be passed <ul>
11376 * <li>The Record block object</li>
11377 * <li>The "arg" argument from the load function</li>
11378 * <li>A boolean success indicator</li>
11380 * @param {Object} scope The scope in which to call the callback
11381 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11383 load : function(params, reader, callback, scope, arg){
11384 if(this.fireEvent("beforeload", this, params) !== false){
11386 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11388 var url = this.url;
11389 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11391 url += "&_dc=" + (new Date().getTime());
11393 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11396 cb : "stcCallback"+transId,
11397 scriptId : "stcScript"+transId,
11401 callback : callback,
11407 window[trans.cb] = function(o){
11408 conn.handleResponse(o, trans);
11411 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11413 if(this.autoAbort !== false){
11417 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11419 var script = document.createElement("script");
11420 script.setAttribute("src", url);
11421 script.setAttribute("type", "text/javascript");
11422 script.setAttribute("id", trans.scriptId);
11423 this.head.appendChild(script);
11425 this.trans = trans;
11427 callback.call(scope||this, null, arg, false);
11432 isLoading : function(){
11433 return this.trans ? true : false;
11437 * Abort the current server request.
11439 abort : function(){
11440 if(this.isLoading()){
11441 this.destroyTrans(this.trans);
11446 destroyTrans : function(trans, isLoaded){
11447 this.head.removeChild(document.getElementById(trans.scriptId));
11448 clearTimeout(trans.timeoutId);
11450 window[trans.cb] = undefined;
11452 delete window[trans.cb];
11455 // if hasn't been loaded, wait for load to remove it to prevent script error
11456 window[trans.cb] = function(){
11457 window[trans.cb] = undefined;
11459 delete window[trans.cb];
11466 handleResponse : function(o, trans){
11467 this.trans = false;
11468 this.destroyTrans(trans, true);
11471 result = trans.reader.readRecords(o);
11473 this.fireEvent("loadexception", this, o, trans.arg, e);
11474 trans.callback.call(trans.scope||window, null, trans.arg, false);
11477 this.fireEvent("load", this, o, trans.arg);
11478 trans.callback.call(trans.scope||window, result, trans.arg, true);
11482 handleFailure : function(trans){
11483 this.trans = false;
11484 this.destroyTrans(trans, false);
11485 this.fireEvent("loadexception", this, null, trans.arg);
11486 trans.callback.call(trans.scope||window, null, trans.arg, false);
11490 * Ext JS Library 1.1.1
11491 * Copyright(c) 2006-2007, Ext JS, LLC.
11493 * Originally Released Under LGPL - original licence link has changed is not relivant.
11496 * <script type="text/javascript">
11500 * @class Roo.data.JsonReader
11501 * @extends Roo.data.DataReader
11502 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11503 * based on mappings in a provided Roo.data.Record constructor.
11505 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11506 * in the reply previously.
11511 var RecordDef = Roo.data.Record.create([
11512 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11513 {name: 'occupation'} // This field will use "occupation" as the mapping.
11515 var myReader = new Roo.data.JsonReader({
11516 totalProperty: "results", // The property which contains the total dataset size (optional)
11517 root: "rows", // The property which contains an Array of row objects
11518 id: "id" // The property within each row object that provides an ID for the record (optional)
11522 * This would consume a JSON file like this:
11524 { 'results': 2, 'rows': [
11525 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11526 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11529 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11530 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11531 * paged from the remote server.
11532 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11533 * @cfg {String} root name of the property which contains the Array of row objects.
11534 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11535 * @cfg {Array} fields Array of field definition objects
11537 * Create a new JsonReader
11538 * @param {Object} meta Metadata configuration options
11539 * @param {Object} recordType Either an Array of field definition objects,
11540 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11542 Roo.data.JsonReader = function(meta, recordType){
11545 // set some defaults:
11546 Roo.applyIf(meta, {
11547 totalProperty: 'total',
11548 successProperty : 'success',
11553 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11555 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11558 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11559 * Used by Store query builder to append _requestMeta to params.
11562 metaFromRemote : false,
11564 * This method is only used by a DataProxy which has retrieved data from a remote server.
11565 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11566 * @return {Object} data A data block which is used by an Roo.data.Store object as
11567 * a cache of Roo.data.Records.
11569 read : function(response){
11570 var json = response.responseText;
11572 var o = /* eval:var:o */ eval("("+json+")");
11574 throw {message: "JsonReader.read: Json object not found"};
11580 this.metaFromRemote = true;
11581 this.meta = o.metaData;
11582 this.recordType = Roo.data.Record.create(o.metaData.fields);
11583 this.onMetaChange(this.meta, this.recordType, o);
11585 return this.readRecords(o);
11588 // private function a store will implement
11589 onMetaChange : function(meta, recordType, o){
11596 simpleAccess: function(obj, subsc) {
11603 getJsonAccessor: function(){
11605 return function(expr) {
11607 return(re.test(expr))
11608 ? new Function("obj", "return obj." + expr)
11613 return Roo.emptyFn;
11618 * Create a data block containing Roo.data.Records from an XML document.
11619 * @param {Object} o An object which contains an Array of row objects in the property specified
11620 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11621 * which contains the total size of the dataset.
11622 * @return {Object} data A data block which is used by an Roo.data.Store object as
11623 * a cache of Roo.data.Records.
11625 readRecords : function(o){
11627 * After any data loads, the raw JSON data is available for further custom processing.
11631 var s = this.meta, Record = this.recordType,
11632 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11634 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11636 if(s.totalProperty) {
11637 this.getTotal = this.getJsonAccessor(s.totalProperty);
11639 if(s.successProperty) {
11640 this.getSuccess = this.getJsonAccessor(s.successProperty);
11642 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11644 var g = this.getJsonAccessor(s.id);
11645 this.getId = function(rec) {
11647 return (r === undefined || r === "") ? null : r;
11650 this.getId = function(){return null;};
11653 for(var jj = 0; jj < fl; jj++){
11655 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11656 this.ef[jj] = this.getJsonAccessor(map);
11660 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11661 if(s.totalProperty){
11662 var vt = parseInt(this.getTotal(o), 10);
11667 if(s.successProperty){
11668 var vs = this.getSuccess(o);
11669 if(vs === false || vs === 'false'){
11674 for(var i = 0; i < c; i++){
11677 var id = this.getId(n);
11678 for(var j = 0; j < fl; j++){
11680 var v = this.ef[j](n);
11682 Roo.log('missing convert for ' + f.name);
11686 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11688 var record = new Record(values, id);
11690 records[i] = record;
11696 totalRecords : totalRecords
11701 * Ext JS Library 1.1.1
11702 * Copyright(c) 2006-2007, Ext JS, LLC.
11704 * Originally Released Under LGPL - original licence link has changed is not relivant.
11707 * <script type="text/javascript">
11711 * @class Roo.data.ArrayReader
11712 * @extends Roo.data.DataReader
11713 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11714 * Each element of that Array represents a row of data fields. The
11715 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11716 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11720 var RecordDef = Roo.data.Record.create([
11721 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11722 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11724 var myReader = new Roo.data.ArrayReader({
11725 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11729 * This would consume an Array like this:
11731 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11733 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11735 * Create a new JsonReader
11736 * @param {Object} meta Metadata configuration options.
11737 * @param {Object} recordType Either an Array of field definition objects
11738 * as specified to {@link Roo.data.Record#create},
11739 * or an {@link Roo.data.Record} object
11740 * created using {@link Roo.data.Record#create}.
11742 Roo.data.ArrayReader = function(meta, recordType){
11743 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11746 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11748 * Create a data block containing Roo.data.Records from an XML document.
11749 * @param {Object} o An Array of row objects which represents the dataset.
11750 * @return {Object} data A data block which is used by an Roo.data.Store object as
11751 * a cache of Roo.data.Records.
11753 readRecords : function(o){
11754 var sid = this.meta ? this.meta.id : null;
11755 var recordType = this.recordType, fields = recordType.prototype.fields;
11758 for(var i = 0; i < root.length; i++){
11761 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11762 for(var j = 0, jlen = fields.length; j < jlen; j++){
11763 var f = fields.items[j];
11764 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11765 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11767 values[f.name] = v;
11769 var record = new recordType(values, id);
11771 records[records.length] = record;
11775 totalRecords : records.length
11784 * @class Roo.bootstrap.ComboBox
11785 * @extends Roo.bootstrap.TriggerField
11786 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11787 * @cfg {Boolean} append (true|false) default false
11788 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11789 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11790 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11791 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11792 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11793 * @cfg {Boolean} animate default true
11794 * @cfg {Boolean} emptyResultText only for touch device
11795 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11797 * Create a new ComboBox.
11798 * @param {Object} config Configuration options
11800 Roo.bootstrap.ComboBox = function(config){
11801 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11805 * Fires when the dropdown list is expanded
11806 * @param {Roo.bootstrap.ComboBox} combo This combo box
11811 * Fires when the dropdown list is collapsed
11812 * @param {Roo.bootstrap.ComboBox} combo This combo box
11816 * @event beforeselect
11817 * Fires before a list item is selected. Return false to cancel the selection.
11818 * @param {Roo.bootstrap.ComboBox} combo This combo box
11819 * @param {Roo.data.Record} record The data record returned from the underlying store
11820 * @param {Number} index The index of the selected item in the dropdown list
11822 'beforeselect' : true,
11825 * Fires when a list item is selected
11826 * @param {Roo.bootstrap.ComboBox} combo This combo box
11827 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11828 * @param {Number} index The index of the selected item in the dropdown list
11832 * @event beforequery
11833 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11834 * The event object passed has these properties:
11835 * @param {Roo.bootstrap.ComboBox} combo This combo box
11836 * @param {String} query The query
11837 * @param {Boolean} forceAll true to force "all" query
11838 * @param {Boolean} cancel true to cancel the query
11839 * @param {Object} e The query event object
11841 'beforequery': true,
11844 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11845 * @param {Roo.bootstrap.ComboBox} combo This combo box
11850 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11851 * @param {Roo.bootstrap.ComboBox} combo This combo box
11852 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11857 * Fires when the remove value from the combobox array
11858 * @param {Roo.bootstrap.ComboBox} combo This combo box
11862 * @event afterremove
11863 * Fires when the remove value from the combobox array
11864 * @param {Roo.bootstrap.ComboBox} combo This combo box
11866 'afterremove' : true,
11868 * @event specialfilter
11869 * Fires when specialfilter
11870 * @param {Roo.bootstrap.ComboBox} combo This combo box
11872 'specialfilter' : true,
11875 * Fires when tick the element
11876 * @param {Roo.bootstrap.ComboBox} combo This combo box
11880 * @event touchviewdisplay
11881 * Fires when touch view require special display (default is using displayField)
11882 * @param {Roo.bootstrap.ComboBox} combo This combo box
11883 * @param {Object} cfg set html .
11885 'touchviewdisplay' : true
11890 this.tickItems = [];
11892 this.selectedIndex = -1;
11893 if(this.mode == 'local'){
11894 if(config.queryDelay === undefined){
11895 this.queryDelay = 10;
11897 if(config.minChars === undefined){
11903 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11906 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11907 * rendering into an Roo.Editor, defaults to false)
11910 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11911 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11914 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11917 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11918 * the dropdown list (defaults to undefined, with no header element)
11922 * @cfg {String/Roo.Template} tpl The template to use to render the output
11926 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11928 listWidth: undefined,
11930 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11931 * mode = 'remote' or 'text' if mode = 'local')
11933 displayField: undefined,
11936 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11937 * mode = 'remote' or 'value' if mode = 'local').
11938 * Note: use of a valueField requires the user make a selection
11939 * in order for a value to be mapped.
11941 valueField: undefined,
11943 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
11948 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11949 * field's data value (defaults to the underlying DOM element's name)
11951 hiddenName: undefined,
11953 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11957 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11959 selectedClass: 'active',
11962 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11966 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11967 * anchor positions (defaults to 'tl-bl')
11969 listAlign: 'tl-bl?',
11971 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11975 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11976 * query specified by the allQuery config option (defaults to 'query')
11978 triggerAction: 'query',
11980 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11981 * (defaults to 4, does not apply if editable = false)
11985 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11986 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11990 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11991 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11995 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11996 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12000 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12001 * when editable = true (defaults to false)
12003 selectOnFocus:false,
12005 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12007 queryParam: 'query',
12009 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12010 * when mode = 'remote' (defaults to 'Loading...')
12012 loadingText: 'Loading...',
12014 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12018 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12022 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12023 * traditional select (defaults to true)
12027 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12031 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12035 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12036 * listWidth has a higher value)
12040 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12041 * allow the user to set arbitrary text into the field (defaults to false)
12043 forceSelection:false,
12045 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12046 * if typeAhead = true (defaults to 250)
12048 typeAheadDelay : 250,
12050 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12051 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12053 valueNotFoundText : undefined,
12055 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12057 blockFocus : false,
12060 * @cfg {Boolean} disableClear Disable showing of clear button.
12062 disableClear : false,
12064 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12066 alwaysQuery : false,
12069 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12074 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12076 invalidClass : "has-warning",
12079 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12081 validClass : "has-success",
12084 * @cfg {Boolean} specialFilter (true|false) special filter default false
12086 specialFilter : false,
12089 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12091 mobileTouchView : true,
12103 btnPosition : 'right',
12104 triggerList : true,
12105 showToggleBtn : true,
12107 emptyResultText: 'Empty',
12108 triggerText : 'Select',
12110 // element that contains real text value.. (when hidden is used..)
12112 getAutoCreate : function()
12120 if(Roo.isTouch && this.mobileTouchView){
12121 cfg = this.getAutoCreateTouchView();
12128 if(!this.tickable){
12129 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12134 * ComboBox with tickable selections
12137 var align = this.labelAlign || this.parentLabelAlign();
12140 cls : 'form-group roo-combobox-tickable' //input-group
12145 cls : 'tickable-buttons',
12150 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12151 html : this.triggerText
12157 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12164 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12171 buttons.cn.unshift({
12173 cls: 'roo-select2-search-field-input'
12179 Roo.each(buttons.cn, function(c){
12181 c.cls += ' btn-' + _this.size;
12184 if (_this.disabled) {
12195 cls: 'form-hidden-field'
12199 cls: 'roo-select2-choices',
12203 cls: 'roo-select2-search-field',
12215 cls: 'roo-select2-container input-group roo-select2-container-multi',
12220 // cls: 'typeahead typeahead-long dropdown-menu',
12221 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12226 if(this.hasFeedback && !this.allowBlank){
12230 cls: 'glyphicon form-control-feedback'
12233 combobox.cn.push(feedback);
12236 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12238 // Roo.log("left and has label");
12244 cls : 'control-label col-sm-' + this.labelWidth,
12245 html : this.fieldLabel
12249 cls : "col-sm-" + (12 - this.labelWidth),
12256 } else if ( this.fieldLabel.length) {
12257 // Roo.log(" label");
12262 //cls : 'input-group-addon',
12263 html : this.fieldLabel
12273 // Roo.log(" no label && no align");
12280 ['xs','sm','md','lg'].map(function(size){
12281 if (settings[size]) {
12282 cfg.cls += ' col-' + size + '-' + settings[size];
12290 _initEventsCalled : false,
12293 initEvents: function()
12296 if (this._initEventsCalled) { // as we call render... prevent looping...
12299 this._initEventsCalled = true;
12302 throw "can not find store for combo";
12305 this.store = Roo.factory(this.store, Roo.data);
12307 // if we are building from html. then this element is so complex, that we can not really
12308 // use the rendered HTML.
12309 // so we have to trash and replace the previous code.
12310 if (Roo.XComponent.build_from_html) {
12312 // remove this element....
12313 var e = this.el.dom, k=0;
12314 while (e ) { e = e.previousSibling; ++k;}
12319 this.rendered = false;
12321 this.render(this.parent().getChildContainer(true), k);
12332 if(Roo.isTouch && this.mobileTouchView){
12333 this.initTouchView();
12338 this.initTickableEvents();
12342 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12344 if(this.hiddenName){
12346 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12348 this.hiddenField.dom.value =
12349 this.hiddenValue !== undefined ? this.hiddenValue :
12350 this.value !== undefined ? this.value : '';
12352 // prevent input submission
12353 this.el.dom.removeAttribute('name');
12354 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12359 // this.el.dom.setAttribute('autocomplete', 'off');
12362 var cls = 'x-combo-list';
12364 //this.list = new Roo.Layer({
12365 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12371 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12372 _this.list.setWidth(lw);
12375 this.list.on('mouseover', this.onViewOver, this);
12376 this.list.on('mousemove', this.onViewMove, this);
12378 this.list.on('scroll', this.onViewScroll, this);
12381 this.list.swallowEvent('mousewheel');
12382 this.assetHeight = 0;
12385 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12386 this.assetHeight += this.header.getHeight();
12389 this.innerList = this.list.createChild({cls:cls+'-inner'});
12390 this.innerList.on('mouseover', this.onViewOver, this);
12391 this.innerList.on('mousemove', this.onViewMove, this);
12392 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12394 if(this.allowBlank && !this.pageSize && !this.disableClear){
12395 this.footer = this.list.createChild({cls:cls+'-ft'});
12396 this.pageTb = new Roo.Toolbar(this.footer);
12400 this.footer = this.list.createChild({cls:cls+'-ft'});
12401 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12402 {pageSize: this.pageSize});
12406 if (this.pageTb && this.allowBlank && !this.disableClear) {
12408 this.pageTb.add(new Roo.Toolbar.Fill(), {
12409 cls: 'x-btn-icon x-btn-clear',
12411 handler: function()
12414 _this.clearValue();
12415 _this.onSelect(false, -1);
12420 this.assetHeight += this.footer.getHeight();
12425 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12428 this.view = new Roo.View(this.list, this.tpl, {
12429 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12431 //this.view.wrapEl.setDisplayed(false);
12432 this.view.on('click', this.onViewClick, this);
12436 this.store.on('beforeload', this.onBeforeLoad, this);
12437 this.store.on('load', this.onLoad, this);
12438 this.store.on('loadexception', this.onLoadException, this);
12440 if(this.resizable){
12441 this.resizer = new Roo.Resizable(this.list, {
12442 pinned:true, handles:'se'
12444 this.resizer.on('resize', function(r, w, h){
12445 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12446 this.listWidth = w;
12447 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12448 this.restrictHeight();
12450 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12453 if(!this.editable){
12454 this.editable = true;
12455 this.setEditable(false);
12460 if (typeof(this.events.add.listeners) != 'undefined') {
12462 this.addicon = this.wrap.createChild(
12463 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12465 this.addicon.on('click', function(e) {
12466 this.fireEvent('add', this);
12469 if (typeof(this.events.edit.listeners) != 'undefined') {
12471 this.editicon = this.wrap.createChild(
12472 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12473 if (this.addicon) {
12474 this.editicon.setStyle('margin-left', '40px');
12476 this.editicon.on('click', function(e) {
12478 // we fire even if inothing is selected..
12479 this.fireEvent('edit', this, this.lastData );
12485 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12486 "up" : function(e){
12487 this.inKeyMode = true;
12491 "down" : function(e){
12492 if(!this.isExpanded()){
12493 this.onTriggerClick();
12495 this.inKeyMode = true;
12500 "enter" : function(e){
12501 // this.onViewClick();
12505 if(this.fireEvent("specialkey", this, e)){
12506 this.onViewClick(false);
12512 "esc" : function(e){
12516 "tab" : function(e){
12519 if(this.fireEvent("specialkey", this, e)){
12520 this.onViewClick(false);
12528 doRelay : function(foo, bar, hname){
12529 if(hname == 'down' || this.scope.isExpanded()){
12530 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12539 this.queryDelay = Math.max(this.queryDelay || 10,
12540 this.mode == 'local' ? 10 : 250);
12543 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12545 if(this.typeAhead){
12546 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12548 if(this.editable !== false){
12549 this.inputEl().on("keyup", this.onKeyUp, this);
12551 if(this.forceSelection){
12552 this.inputEl().on('blur', this.doForce, this);
12556 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12557 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12561 initTickableEvents: function()
12565 if(this.hiddenName){
12567 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12569 this.hiddenField.dom.value =
12570 this.hiddenValue !== undefined ? this.hiddenValue :
12571 this.value !== undefined ? this.value : '';
12573 // prevent input submission
12574 this.el.dom.removeAttribute('name');
12575 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12580 // this.list = this.el.select('ul.dropdown-menu',true).first();
12582 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12583 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12584 if(this.triggerList){
12585 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12588 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12589 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12591 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12592 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12594 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12595 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12597 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12598 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12599 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12602 this.cancelBtn.hide();
12607 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12608 _this.list.setWidth(lw);
12611 this.list.on('mouseover', this.onViewOver, this);
12612 this.list.on('mousemove', this.onViewMove, this);
12614 this.list.on('scroll', this.onViewScroll, this);
12617 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>';
12620 this.view = new Roo.View(this.list, this.tpl, {
12621 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12624 //this.view.wrapEl.setDisplayed(false);
12625 this.view.on('click', this.onViewClick, this);
12629 this.store.on('beforeload', this.onBeforeLoad, this);
12630 this.store.on('load', this.onLoad, this);
12631 this.store.on('loadexception', this.onLoadException, this);
12634 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12635 "up" : function(e){
12636 this.inKeyMode = true;
12640 "down" : function(e){
12641 this.inKeyMode = true;
12645 "enter" : function(e){
12646 if(this.fireEvent("specialkey", this, e)){
12647 this.onViewClick(false);
12653 "esc" : function(e){
12654 this.onTickableFooterButtonClick(e, false, false);
12657 "tab" : function(e){
12658 this.fireEvent("specialkey", this, e);
12660 this.onTickableFooterButtonClick(e, false, false);
12667 doRelay : function(e, fn, key){
12668 if(this.scope.isExpanded()){
12669 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12678 this.queryDelay = Math.max(this.queryDelay || 10,
12679 this.mode == 'local' ? 10 : 250);
12682 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12684 if(this.typeAhead){
12685 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12688 if(this.editable !== false){
12689 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12694 onDestroy : function(){
12696 this.view.setStore(null);
12697 this.view.el.removeAllListeners();
12698 this.view.el.remove();
12699 this.view.purgeListeners();
12702 this.list.dom.innerHTML = '';
12706 this.store.un('beforeload', this.onBeforeLoad, this);
12707 this.store.un('load', this.onLoad, this);
12708 this.store.un('loadexception', this.onLoadException, this);
12710 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12714 fireKey : function(e){
12715 if(e.isNavKeyPress() && !this.list.isVisible()){
12716 this.fireEvent("specialkey", this, e);
12721 onResize: function(w, h){
12722 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12724 // if(typeof w != 'number'){
12725 // // we do not handle it!?!?
12728 // var tw = this.trigger.getWidth();
12729 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12730 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12732 // this.inputEl().setWidth( this.adjustWidth('input', x));
12734 // //this.trigger.setStyle('left', x+'px');
12736 // if(this.list && this.listWidth === undefined){
12737 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12738 // this.list.setWidth(lw);
12739 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12747 * Allow or prevent the user from directly editing the field text. If false is passed,
12748 * the user will only be able to select from the items defined in the dropdown list. This method
12749 * is the runtime equivalent of setting the 'editable' config option at config time.
12750 * @param {Boolean} value True to allow the user to directly edit the field text
12752 setEditable : function(value){
12753 if(value == this.editable){
12756 this.editable = value;
12758 this.inputEl().dom.setAttribute('readOnly', true);
12759 this.inputEl().on('mousedown', this.onTriggerClick, this);
12760 this.inputEl().addClass('x-combo-noedit');
12762 this.inputEl().dom.setAttribute('readOnly', false);
12763 this.inputEl().un('mousedown', this.onTriggerClick, this);
12764 this.inputEl().removeClass('x-combo-noedit');
12770 onBeforeLoad : function(combo,opts){
12771 if(!this.hasFocus){
12775 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12777 this.restrictHeight();
12778 this.selectedIndex = -1;
12782 onLoad : function(){
12784 this.hasQuery = false;
12786 if(!this.hasFocus){
12790 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12791 this.loading.hide();
12794 if(this.store.getCount() > 0){
12796 this.restrictHeight();
12797 if(this.lastQuery == this.allQuery){
12798 if(this.editable && !this.tickable){
12799 this.inputEl().dom.select();
12803 !this.selectByValue(this.value, true) &&
12806 !this.store.lastOptions ||
12807 typeof(this.store.lastOptions.add) == 'undefined' ||
12808 this.store.lastOptions.add != true
12811 this.select(0, true);
12814 if(this.autoFocus){
12817 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12818 this.taTask.delay(this.typeAheadDelay);
12822 this.onEmptyResults();
12828 onLoadException : function()
12830 this.hasQuery = false;
12832 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12833 this.loading.hide();
12836 if(this.tickable && this.editable){
12841 // only causes errors at present
12842 //Roo.log(this.store.reader.jsonData);
12843 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12845 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12851 onTypeAhead : function(){
12852 if(this.store.getCount() > 0){
12853 var r = this.store.getAt(0);
12854 var newValue = r.data[this.displayField];
12855 var len = newValue.length;
12856 var selStart = this.getRawValue().length;
12858 if(selStart != len){
12859 this.setRawValue(newValue);
12860 this.selectText(selStart, newValue.length);
12866 onSelect : function(record, index){
12868 if(this.fireEvent('beforeselect', this, record, index) !== false){
12870 this.setFromData(index > -1 ? record.data : false);
12873 this.fireEvent('select', this, record, index);
12878 * Returns the currently selected field value or empty string if no value is set.
12879 * @return {String} value The selected value
12881 getValue : function(){
12884 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12887 if(this.valueField){
12888 return typeof this.value != 'undefined' ? this.value : '';
12890 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12895 * Clears any text/value currently set in the field
12897 clearValue : function(){
12898 if(this.hiddenField){
12899 this.hiddenField.dom.value = '';
12902 this.setRawValue('');
12903 this.lastSelectionText = '';
12904 this.lastData = false;
12906 var close = this.closeTriggerEl();
12915 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12916 * will be displayed in the field. If the value does not match the data value of an existing item,
12917 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12918 * Otherwise the field will be blank (although the value will still be set).
12919 * @param {String} value The value to match
12921 setValue : function(v){
12928 if(this.valueField){
12929 var r = this.findRecord(this.valueField, v);
12931 text = r.data[this.displayField];
12932 }else if(this.valueNotFoundText !== undefined){
12933 text = this.valueNotFoundText;
12936 this.lastSelectionText = text;
12937 if(this.hiddenField){
12938 this.hiddenField.dom.value = v;
12940 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12943 var close = this.closeTriggerEl();
12946 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12950 * @property {Object} the last set data for the element
12955 * Sets the value of the field based on a object which is related to the record format for the store.
12956 * @param {Object} value the value to set as. or false on reset?
12958 setFromData : function(o){
12965 var dv = ''; // display value
12966 var vv = ''; // value value..
12968 if (this.displayField) {
12969 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12971 // this is an error condition!!!
12972 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12975 if(this.valueField){
12976 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12979 var close = this.closeTriggerEl();
12982 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12985 if(this.hiddenField){
12986 this.hiddenField.dom.value = vv;
12988 this.lastSelectionText = dv;
12989 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12993 // no hidden field.. - we store the value in 'value', but still display
12994 // display field!!!!
12995 this.lastSelectionText = dv;
12996 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13003 reset : function(){
13004 // overridden so that last data is reset..
13011 this.setValue(this.originalValue);
13012 this.clearInvalid();
13013 this.lastData = false;
13015 this.view.clearSelections();
13019 findRecord : function(prop, value){
13021 if(this.store.getCount() > 0){
13022 this.store.each(function(r){
13023 if(r.data[prop] == value){
13033 getName: function()
13035 // returns hidden if it's set..
13036 if (!this.rendered) {return ''};
13037 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13041 onViewMove : function(e, t){
13042 this.inKeyMode = false;
13046 onViewOver : function(e, t){
13047 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13050 var item = this.view.findItemFromChild(t);
13053 var index = this.view.indexOf(item);
13054 this.select(index, false);
13059 onViewClick : function(view, doFocus, el, e)
13061 var index = this.view.getSelectedIndexes()[0];
13063 var r = this.store.getAt(index);
13067 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13074 Roo.each(this.tickItems, function(v,k){
13076 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13078 _this.tickItems.splice(k, 1);
13080 if(typeof(e) == 'undefined' && view == false){
13081 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13093 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13094 this.tickItems.push(r.data);
13097 if(typeof(e) == 'undefined' && view == false){
13098 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13105 this.onSelect(r, index);
13107 if(doFocus !== false && !this.blockFocus){
13108 this.inputEl().focus();
13113 restrictHeight : function(){
13114 //this.innerList.dom.style.height = '';
13115 //var inner = this.innerList.dom;
13116 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13117 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13118 //this.list.beginUpdate();
13119 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13120 this.list.alignTo(this.inputEl(), this.listAlign);
13121 this.list.alignTo(this.inputEl(), this.listAlign);
13122 //this.list.endUpdate();
13126 onEmptyResults : function(){
13128 if(this.tickable && this.editable){
13129 this.restrictHeight();
13137 * Returns true if the dropdown list is expanded, else false.
13139 isExpanded : function(){
13140 return this.list.isVisible();
13144 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13145 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13146 * @param {String} value The data value of the item to select
13147 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13148 * selected item if it is not currently in view (defaults to true)
13149 * @return {Boolean} True if the value matched an item in the list, else false
13151 selectByValue : function(v, scrollIntoView){
13152 if(v !== undefined && v !== null){
13153 var r = this.findRecord(this.valueField || this.displayField, v);
13155 this.select(this.store.indexOf(r), scrollIntoView);
13163 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13164 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13165 * @param {Number} index The zero-based index of the list item to select
13166 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13167 * selected item if it is not currently in view (defaults to true)
13169 select : function(index, scrollIntoView){
13170 this.selectedIndex = index;
13171 this.view.select(index);
13172 if(scrollIntoView !== false){
13173 var el = this.view.getNode(index);
13175 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13178 this.list.scrollChildIntoView(el, false);
13184 selectNext : function(){
13185 var ct = this.store.getCount();
13187 if(this.selectedIndex == -1){
13189 }else if(this.selectedIndex < ct-1){
13190 this.select(this.selectedIndex+1);
13196 selectPrev : function(){
13197 var ct = this.store.getCount();
13199 if(this.selectedIndex == -1){
13201 }else if(this.selectedIndex != 0){
13202 this.select(this.selectedIndex-1);
13208 onKeyUp : function(e){
13209 if(this.editable !== false && !e.isSpecialKey()){
13210 this.lastKey = e.getKey();
13211 this.dqTask.delay(this.queryDelay);
13216 validateBlur : function(){
13217 return !this.list || !this.list.isVisible();
13221 initQuery : function(){
13223 var v = this.getRawValue();
13225 if(this.tickable && this.editable){
13226 v = this.tickableInputEl().getValue();
13233 doForce : function(){
13234 if(this.inputEl().dom.value.length > 0){
13235 this.inputEl().dom.value =
13236 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13242 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13243 * query allowing the query action to be canceled if needed.
13244 * @param {String} query The SQL query to execute
13245 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13246 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13247 * saved in the current store (defaults to false)
13249 doQuery : function(q, forceAll){
13251 if(q === undefined || q === null){
13256 forceAll: forceAll,
13260 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13265 forceAll = qe.forceAll;
13266 if(forceAll === true || (q.length >= this.minChars)){
13268 this.hasQuery = true;
13270 if(this.lastQuery != q || this.alwaysQuery){
13271 this.lastQuery = q;
13272 if(this.mode == 'local'){
13273 this.selectedIndex = -1;
13275 this.store.clearFilter();
13278 if(this.specialFilter){
13279 this.fireEvent('specialfilter', this);
13284 this.store.filter(this.displayField, q);
13287 this.store.fireEvent("datachanged", this.store);
13294 this.store.baseParams[this.queryParam] = q;
13296 var options = {params : this.getParams(q)};
13299 options.add = true;
13300 options.params.start = this.page * this.pageSize;
13303 this.store.load(options);
13306 * this code will make the page width larger, at the beginning, the list not align correctly,
13307 * we should expand the list on onLoad
13308 * so command out it
13313 this.selectedIndex = -1;
13318 this.loadNext = false;
13322 getParams : function(q){
13324 //p[this.queryParam] = q;
13328 p.limit = this.pageSize;
13334 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13336 collapse : function(){
13337 if(!this.isExpanded()){
13344 this.hasFocus = false;
13346 this.cancelBtn.hide();
13347 this.trigger.show();
13350 this.tickableInputEl().dom.value = '';
13351 this.tickableInputEl().blur();
13356 Roo.get(document).un('mousedown', this.collapseIf, this);
13357 Roo.get(document).un('mousewheel', this.collapseIf, this);
13358 if (!this.editable) {
13359 Roo.get(document).un('keydown', this.listKeyPress, this);
13361 this.fireEvent('collapse', this);
13365 collapseIf : function(e){
13366 var in_combo = e.within(this.el);
13367 var in_list = e.within(this.list);
13368 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13370 if (in_combo || in_list || is_list) {
13371 //e.stopPropagation();
13376 this.onTickableFooterButtonClick(e, false, false);
13384 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13386 expand : function(){
13388 if(this.isExpanded() || !this.hasFocus){
13392 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13393 this.list.setWidth(lw);
13400 this.restrictHeight();
13404 this.tickItems = Roo.apply([], this.item);
13407 this.cancelBtn.show();
13408 this.trigger.hide();
13411 this.tickableInputEl().focus();
13416 Roo.get(document).on('mousedown', this.collapseIf, this);
13417 Roo.get(document).on('mousewheel', this.collapseIf, this);
13418 if (!this.editable) {
13419 Roo.get(document).on('keydown', this.listKeyPress, this);
13422 this.fireEvent('expand', this);
13426 // Implements the default empty TriggerField.onTriggerClick function
13427 onTriggerClick : function(e)
13429 Roo.log('trigger click');
13431 if(this.disabled || !this.triggerList){
13436 this.loadNext = false;
13438 if(this.isExpanded()){
13440 if (!this.blockFocus) {
13441 this.inputEl().focus();
13445 this.hasFocus = true;
13446 if(this.triggerAction == 'all') {
13447 this.doQuery(this.allQuery, true);
13449 this.doQuery(this.getRawValue());
13451 if (!this.blockFocus) {
13452 this.inputEl().focus();
13457 onTickableTriggerClick : function(e)
13464 this.loadNext = false;
13465 this.hasFocus = true;
13467 if(this.triggerAction == 'all') {
13468 this.doQuery(this.allQuery, true);
13470 this.doQuery(this.getRawValue());
13474 onSearchFieldClick : function(e)
13476 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13477 this.onTickableFooterButtonClick(e, false, false);
13481 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13486 this.loadNext = false;
13487 this.hasFocus = true;
13489 if(this.triggerAction == 'all') {
13490 this.doQuery(this.allQuery, true);
13492 this.doQuery(this.getRawValue());
13496 listKeyPress : function(e)
13498 //Roo.log('listkeypress');
13499 // scroll to first matching element based on key pres..
13500 if (e.isSpecialKey()) {
13503 var k = String.fromCharCode(e.getKey()).toUpperCase();
13506 var csel = this.view.getSelectedNodes();
13507 var cselitem = false;
13509 var ix = this.view.indexOf(csel[0]);
13510 cselitem = this.store.getAt(ix);
13511 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13517 this.store.each(function(v) {
13519 // start at existing selection.
13520 if (cselitem.id == v.id) {
13526 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13527 match = this.store.indexOf(v);
13533 if (match === false) {
13534 return true; // no more action?
13537 this.view.select(match);
13538 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13539 sn.scrollIntoView(sn.dom.parentNode, false);
13542 onViewScroll : function(e, t){
13544 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){
13548 this.hasQuery = true;
13550 this.loading = this.list.select('.loading', true).first();
13552 if(this.loading === null){
13553 this.list.createChild({
13555 cls: 'loading roo-select2-more-results roo-select2-active',
13556 html: 'Loading more results...'
13559 this.loading = this.list.select('.loading', true).first();
13561 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13563 this.loading.hide();
13566 this.loading.show();
13571 this.loadNext = true;
13573 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13578 addItem : function(o)
13580 var dv = ''; // display value
13582 if (this.displayField) {
13583 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13585 // this is an error condition!!!
13586 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13593 var choice = this.choices.createChild({
13595 cls: 'roo-select2-search-choice',
13604 cls: 'roo-select2-search-choice-close',
13609 }, this.searchField);
13611 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13613 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13621 this.inputEl().dom.value = '';
13626 onRemoveItem : function(e, _self, o)
13628 e.preventDefault();
13630 this.lastItem = Roo.apply([], this.item);
13632 var index = this.item.indexOf(o.data) * 1;
13635 Roo.log('not this item?!');
13639 this.item.splice(index, 1);
13644 this.fireEvent('remove', this, e);
13650 syncValue : function()
13652 if(!this.item.length){
13659 Roo.each(this.item, function(i){
13660 if(_this.valueField){
13661 value.push(i[_this.valueField]);
13668 this.value = value.join(',');
13670 if(this.hiddenField){
13671 this.hiddenField.dom.value = this.value;
13674 this.store.fireEvent("datachanged", this.store);
13677 clearItem : function()
13679 if(!this.multiple){
13685 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13693 if(this.tickable && !Roo.isTouch){
13694 this.view.refresh();
13698 inputEl: function ()
13700 if(Roo.isTouch && this.mobileTouchView){
13701 return this.el.select('input.form-control',true).first();
13705 return this.searchField;
13708 return this.el.select('input.form-control',true).first();
13712 onTickableFooterButtonClick : function(e, btn, el)
13714 e.preventDefault();
13716 this.lastItem = Roo.apply([], this.item);
13718 if(btn && btn.name == 'cancel'){
13719 this.tickItems = Roo.apply([], this.item);
13728 Roo.each(this.tickItems, function(o){
13736 validate : function()
13738 var v = this.getRawValue();
13741 v = this.getValue();
13744 if(this.disabled || this.allowBlank || v.length){
13749 this.markInvalid();
13753 tickableInputEl : function()
13755 if(!this.tickable || !this.editable){
13756 return this.inputEl();
13759 return this.inputEl().select('.roo-select2-search-field-input', true).first();
13763 getAutoCreateTouchView : function()
13768 cls: 'form-group' //input-group
13774 type : this.inputType,
13775 cls : 'form-control x-combo-noedit',
13776 autocomplete: 'new-password',
13777 placeholder : this.placeholder || '',
13782 input.name = this.name;
13786 input.cls += ' input-' + this.size;
13789 if (this.disabled) {
13790 input.disabled = true;
13801 inputblock.cls += ' input-group';
13803 inputblock.cn.unshift({
13805 cls : 'input-group-addon',
13810 if(this.removable && !this.multiple){
13811 inputblock.cls += ' roo-removable';
13813 inputblock.cn.push({
13816 cls : 'roo-combo-removable-btn close'
13820 if(this.hasFeedback && !this.allowBlank){
13822 inputblock.cls += ' has-feedback';
13824 inputblock.cn.push({
13826 cls: 'glyphicon form-control-feedback'
13833 inputblock.cls += (this.before) ? '' : ' input-group';
13835 inputblock.cn.push({
13837 cls : 'input-group-addon',
13848 cls: 'form-hidden-field'
13862 cls: 'form-hidden-field'
13866 cls: 'roo-select2-choices',
13870 cls: 'roo-select2-search-field',
13883 cls: 'roo-select2-container input-group roo-touchview-combobox ',
13889 if(!this.multiple && this.showToggleBtn){
13896 if (this.caret != false) {
13899 cls: 'fa fa-' + this.caret
13906 cls : 'input-group-addon btn dropdown-toggle',
13911 cls: 'combobox-clear',
13925 combobox.cls += ' roo-select2-container-multi';
13928 var align = this.labelAlign || this.parentLabelAlign();
13932 if(this.fieldLabel.length && this.labelWidth){
13934 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13935 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13940 cls : 'control-label ' + lw,
13941 html : this.fieldLabel
13953 var settings = this;
13955 ['xs','sm','md','lg'].map(function(size){
13956 if (settings[size]) {
13957 cfg.cls += ' col-' + size + '-' + settings[size];
13964 initTouchView : function()
13966 this.renderTouchView();
13968 this.touchViewEl.on('scroll', function(){
13969 this.el.dom.scrollTop = 0;
13972 this.originalValue = this.getValue();
13974 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
13976 this.inputEl().on("click", this.showTouchView, this);
13977 this.triggerEl.on("click", this.showTouchView, this);
13979 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13980 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13982 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13984 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13985 this.store.on('load', this.onTouchViewLoad, this);
13986 this.store.on('loadexception', this.onTouchViewLoadException, this);
13988 if(this.hiddenName){
13990 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13992 this.hiddenField.dom.value =
13993 this.hiddenValue !== undefined ? this.hiddenValue :
13994 this.value !== undefined ? this.value : '';
13996 this.el.dom.removeAttribute('name');
13997 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14001 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14002 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14005 if(this.removable && !this.multiple){
14006 var close = this.closeTriggerEl();
14008 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14009 close.on('click', this.removeBtnClick, this, close);
14013 * fix the bug in Safari iOS8
14015 this.inputEl().on("focus", function(e){
14016 document.activeElement.blur();
14024 renderTouchView : function()
14026 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14027 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14029 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14030 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14032 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14033 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14034 this.touchViewBodyEl.setStyle('overflow', 'auto');
14036 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14037 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14039 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14040 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14044 showTouchView : function()
14050 this.touchViewHeaderEl.hide();
14052 if(this.modalTitle.length){
14053 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14054 this.touchViewHeaderEl.show();
14057 this.touchViewEl.show();
14059 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14060 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14061 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14063 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14065 if(this.modalTitle.length){
14066 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14069 this.touchViewBodyEl.setHeight(bodyHeight);
14073 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14075 this.touchViewEl.addClass('in');
14078 this.doTouchViewQuery();
14082 hideTouchView : function()
14084 this.touchViewEl.removeClass('in');
14088 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14090 this.touchViewEl.setStyle('display', 'none');
14095 setTouchViewValue : function()
14102 Roo.each(this.tickItems, function(o){
14107 this.hideTouchView();
14110 doTouchViewQuery : function()
14119 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14123 if(!this.alwaysQuery || this.mode == 'local'){
14124 this.onTouchViewLoad();
14131 onTouchViewBeforeLoad : function(combo,opts)
14137 onTouchViewLoad : function()
14139 if(this.store.getCount() < 1){
14140 this.onTouchViewEmptyResults();
14144 this.clearTouchView();
14146 var rawValue = this.getRawValue();
14148 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14150 this.tickItems = [];
14152 this.store.data.each(function(d, rowIndex){
14153 var row = this.touchViewListGroup.createChild(template);
14155 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14156 row.addClass(d.data.cls);
14159 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14162 html : d.data[this.displayField]
14165 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14166 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14169 row.removeClass('selected');
14170 if(!this.multiple && this.valueField &&
14171 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14174 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14175 row.addClass('selected');
14178 if(this.multiple && this.valueField &&
14179 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14183 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14184 this.tickItems.push(d.data);
14187 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14191 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14193 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14195 if(this.modalTitle.length){
14196 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14199 var listHeight = this.touchViewListGroup.getHeight();
14203 if(firstChecked && listHeight > bodyHeight){
14204 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14209 onTouchViewLoadException : function()
14211 this.hideTouchView();
14214 onTouchViewEmptyResults : function()
14216 this.clearTouchView();
14218 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14220 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14224 clearTouchView : function()
14226 this.touchViewListGroup.dom.innerHTML = '';
14229 onTouchViewClick : function(e, el, o)
14231 e.preventDefault();
14234 var rowIndex = o.rowIndex;
14236 var r = this.store.getAt(rowIndex);
14238 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14240 if(!this.multiple){
14241 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14242 c.dom.removeAttribute('checked');
14245 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14247 this.setFromData(r.data);
14249 var close = this.closeTriggerEl();
14255 this.hideTouchView();
14257 this.fireEvent('select', this, r, rowIndex);
14262 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14263 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14264 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14268 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14269 this.addItem(r.data);
14270 this.tickItems.push(r.data);
14276 * @cfg {Boolean} grow
14280 * @cfg {Number} growMin
14284 * @cfg {Number} growMax
14293 Roo.apply(Roo.bootstrap.ComboBox, {
14297 cls: 'modal-header',
14319 cls: 'list-group-item',
14323 cls: 'roo-combobox-list-group-item-value'
14327 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14341 listItemCheckbox : {
14343 cls: 'list-group-item',
14347 cls: 'roo-combobox-list-group-item-value'
14351 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14367 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14372 cls: 'modal-footer',
14380 cls: 'col-xs-6 text-left',
14383 cls: 'btn btn-danger roo-touch-view-cancel',
14389 cls: 'col-xs-6 text-right',
14392 cls: 'btn btn-success roo-touch-view-ok',
14403 Roo.apply(Roo.bootstrap.ComboBox, {
14405 touchViewTemplate : {
14407 cls: 'modal fade roo-combobox-touch-view',
14411 cls: 'modal-dialog',
14412 style : 'position:fixed', // we have to fix position....
14416 cls: 'modal-content',
14418 Roo.bootstrap.ComboBox.header,
14419 Roo.bootstrap.ComboBox.body,
14420 Roo.bootstrap.ComboBox.footer
14429 * Ext JS Library 1.1.1
14430 * Copyright(c) 2006-2007, Ext JS, LLC.
14432 * Originally Released Under LGPL - original licence link has changed is not relivant.
14435 * <script type="text/javascript">
14440 * @extends Roo.util.Observable
14441 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14442 * This class also supports single and multi selection modes. <br>
14443 * Create a data model bound view:
14445 var store = new Roo.data.Store(...);
14447 var view = new Roo.View({
14449 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14451 singleSelect: true,
14452 selectedClass: "ydataview-selected",
14456 // listen for node click?
14457 view.on("click", function(vw, index, node, e){
14458 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14462 dataModel.load("foobar.xml");
14464 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14466 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14467 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14469 * Note: old style constructor is still suported (container, template, config)
14472 * Create a new View
14473 * @param {Object} config The config object
14476 Roo.View = function(config, depreciated_tpl, depreciated_config){
14478 this.parent = false;
14480 if (typeof(depreciated_tpl) == 'undefined') {
14481 // new way.. - universal constructor.
14482 Roo.apply(this, config);
14483 this.el = Roo.get(this.el);
14486 this.el = Roo.get(config);
14487 this.tpl = depreciated_tpl;
14488 Roo.apply(this, depreciated_config);
14490 this.wrapEl = this.el.wrap().wrap();
14491 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14494 if(typeof(this.tpl) == "string"){
14495 this.tpl = new Roo.Template(this.tpl);
14497 // support xtype ctors..
14498 this.tpl = new Roo.factory(this.tpl, Roo);
14502 this.tpl.compile();
14507 * @event beforeclick
14508 * Fires before a click is processed. Returns false to cancel the default action.
14509 * @param {Roo.View} this
14510 * @param {Number} index The index of the target node
14511 * @param {HTMLElement} node The target node
14512 * @param {Roo.EventObject} e The raw event object
14514 "beforeclick" : true,
14517 * Fires when a template node is clicked.
14518 * @param {Roo.View} this
14519 * @param {Number} index The index of the target node
14520 * @param {HTMLElement} node The target node
14521 * @param {Roo.EventObject} e The raw event object
14526 * Fires when a template node is double clicked.
14527 * @param {Roo.View} this
14528 * @param {Number} index The index of the target node
14529 * @param {HTMLElement} node The target node
14530 * @param {Roo.EventObject} e The raw event object
14534 * @event contextmenu
14535 * Fires when a template node is right clicked.
14536 * @param {Roo.View} this
14537 * @param {Number} index The index of the target node
14538 * @param {HTMLElement} node The target node
14539 * @param {Roo.EventObject} e The raw event object
14541 "contextmenu" : true,
14543 * @event selectionchange
14544 * Fires when the selected nodes change.
14545 * @param {Roo.View} this
14546 * @param {Array} selections Array of the selected nodes
14548 "selectionchange" : true,
14551 * @event beforeselect
14552 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14553 * @param {Roo.View} this
14554 * @param {HTMLElement} node The node to be selected
14555 * @param {Array} selections Array of currently selected nodes
14557 "beforeselect" : true,
14559 * @event preparedata
14560 * Fires on every row to render, to allow you to change the data.
14561 * @param {Roo.View} this
14562 * @param {Object} data to be rendered (change this)
14564 "preparedata" : true
14572 "click": this.onClick,
14573 "dblclick": this.onDblClick,
14574 "contextmenu": this.onContextMenu,
14578 this.selections = [];
14580 this.cmp = new Roo.CompositeElementLite([]);
14582 this.store = Roo.factory(this.store, Roo.data);
14583 this.setStore(this.store, true);
14586 if ( this.footer && this.footer.xtype) {
14588 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14590 this.footer.dataSource = this.store;
14591 this.footer.container = fctr;
14592 this.footer = Roo.factory(this.footer, Roo);
14593 fctr.insertFirst(this.el);
14595 // this is a bit insane - as the paging toolbar seems to detach the el..
14596 // dom.parentNode.parentNode.parentNode
14597 // they get detached?
14601 Roo.View.superclass.constructor.call(this);
14606 Roo.extend(Roo.View, Roo.util.Observable, {
14609 * @cfg {Roo.data.Store} store Data store to load data from.
14614 * @cfg {String|Roo.Element} el The container element.
14619 * @cfg {String|Roo.Template} tpl The template used by this View
14623 * @cfg {String} dataName the named area of the template to use as the data area
14624 * Works with domtemplates roo-name="name"
14628 * @cfg {String} selectedClass The css class to add to selected nodes
14630 selectedClass : "x-view-selected",
14632 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14637 * @cfg {String} text to display on mask (default Loading)
14641 * @cfg {Boolean} multiSelect Allow multiple selection
14643 multiSelect : false,
14645 * @cfg {Boolean} singleSelect Allow single selection
14647 singleSelect: false,
14650 * @cfg {Boolean} toggleSelect - selecting
14652 toggleSelect : false,
14655 * @cfg {Boolean} tickable - selecting
14660 * Returns the element this view is bound to.
14661 * @return {Roo.Element}
14663 getEl : function(){
14664 return this.wrapEl;
14670 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14672 refresh : function(){
14673 //Roo.log('refresh');
14676 // if we are using something like 'domtemplate', then
14677 // the what gets used is:
14678 // t.applySubtemplate(NAME, data, wrapping data..)
14679 // the outer template then get' applied with
14680 // the store 'extra data'
14681 // and the body get's added to the
14682 // roo-name="data" node?
14683 // <span class='roo-tpl-{name}'></span> ?????
14687 this.clearSelections();
14688 this.el.update("");
14690 var records = this.store.getRange();
14691 if(records.length < 1) {
14693 // is this valid?? = should it render a template??
14695 this.el.update(this.emptyText);
14699 if (this.dataName) {
14700 this.el.update(t.apply(this.store.meta)); //????
14701 el = this.el.child('.roo-tpl-' + this.dataName);
14704 for(var i = 0, len = records.length; i < len; i++){
14705 var data = this.prepareData(records[i].data, i, records[i]);
14706 this.fireEvent("preparedata", this, data, i, records[i]);
14708 var d = Roo.apply({}, data);
14711 Roo.apply(d, {'roo-id' : Roo.id()});
14715 Roo.each(this.parent.item, function(item){
14716 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14719 Roo.apply(d, {'roo-data-checked' : 'checked'});
14723 html[html.length] = Roo.util.Format.trim(
14725 t.applySubtemplate(this.dataName, d, this.store.meta) :
14732 el.update(html.join(""));
14733 this.nodes = el.dom.childNodes;
14734 this.updateIndexes(0);
14739 * Function to override to reformat the data that is sent to
14740 * the template for each node.
14741 * DEPRICATED - use the preparedata event handler.
14742 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14743 * a JSON object for an UpdateManager bound view).
14745 prepareData : function(data, index, record)
14747 this.fireEvent("preparedata", this, data, index, record);
14751 onUpdate : function(ds, record){
14752 // Roo.log('on update');
14753 this.clearSelections();
14754 var index = this.store.indexOf(record);
14755 var n = this.nodes[index];
14756 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14757 n.parentNode.removeChild(n);
14758 this.updateIndexes(index, index);
14764 onAdd : function(ds, records, index)
14766 //Roo.log(['on Add', ds, records, index] );
14767 this.clearSelections();
14768 if(this.nodes.length == 0){
14772 var n = this.nodes[index];
14773 for(var i = 0, len = records.length; i < len; i++){
14774 var d = this.prepareData(records[i].data, i, records[i]);
14776 this.tpl.insertBefore(n, d);
14779 this.tpl.append(this.el, d);
14782 this.updateIndexes(index);
14785 onRemove : function(ds, record, index){
14786 // Roo.log('onRemove');
14787 this.clearSelections();
14788 var el = this.dataName ?
14789 this.el.child('.roo-tpl-' + this.dataName) :
14792 el.dom.removeChild(this.nodes[index]);
14793 this.updateIndexes(index);
14797 * Refresh an individual node.
14798 * @param {Number} index
14800 refreshNode : function(index){
14801 this.onUpdate(this.store, this.store.getAt(index));
14804 updateIndexes : function(startIndex, endIndex){
14805 var ns = this.nodes;
14806 startIndex = startIndex || 0;
14807 endIndex = endIndex || ns.length - 1;
14808 for(var i = startIndex; i <= endIndex; i++){
14809 ns[i].nodeIndex = i;
14814 * Changes the data store this view uses and refresh the view.
14815 * @param {Store} store
14817 setStore : function(store, initial){
14818 if(!initial && this.store){
14819 this.store.un("datachanged", this.refresh);
14820 this.store.un("add", this.onAdd);
14821 this.store.un("remove", this.onRemove);
14822 this.store.un("update", this.onUpdate);
14823 this.store.un("clear", this.refresh);
14824 this.store.un("beforeload", this.onBeforeLoad);
14825 this.store.un("load", this.onLoad);
14826 this.store.un("loadexception", this.onLoad);
14830 store.on("datachanged", this.refresh, this);
14831 store.on("add", this.onAdd, this);
14832 store.on("remove", this.onRemove, this);
14833 store.on("update", this.onUpdate, this);
14834 store.on("clear", this.refresh, this);
14835 store.on("beforeload", this.onBeforeLoad, this);
14836 store.on("load", this.onLoad, this);
14837 store.on("loadexception", this.onLoad, this);
14845 * onbeforeLoad - masks the loading area.
14848 onBeforeLoad : function(store,opts)
14850 //Roo.log('onBeforeLoad');
14852 this.el.update("");
14854 this.el.mask(this.mask ? this.mask : "Loading" );
14856 onLoad : function ()
14863 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14864 * @param {HTMLElement} node
14865 * @return {HTMLElement} The template node
14867 findItemFromChild : function(node){
14868 var el = this.dataName ?
14869 this.el.child('.roo-tpl-' + this.dataName,true) :
14872 if(!node || node.parentNode == el){
14875 var p = node.parentNode;
14876 while(p && p != el){
14877 if(p.parentNode == el){
14886 onClick : function(e){
14887 var item = this.findItemFromChild(e.getTarget());
14889 var index = this.indexOf(item);
14890 if(this.onItemClick(item, index, e) !== false){
14891 this.fireEvent("click", this, index, item, e);
14894 this.clearSelections();
14899 onContextMenu : function(e){
14900 var item = this.findItemFromChild(e.getTarget());
14902 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14907 onDblClick : function(e){
14908 var item = this.findItemFromChild(e.getTarget());
14910 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14914 onItemClick : function(item, index, e)
14916 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14919 if (this.toggleSelect) {
14920 var m = this.isSelected(item) ? 'unselect' : 'select';
14923 _t[m](item, true, false);
14926 if(this.multiSelect || this.singleSelect){
14927 if(this.multiSelect && e.shiftKey && this.lastSelection){
14928 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14930 this.select(item, this.multiSelect && e.ctrlKey);
14931 this.lastSelection = item;
14934 if(!this.tickable){
14935 e.preventDefault();
14943 * Get the number of selected nodes.
14946 getSelectionCount : function(){
14947 return this.selections.length;
14951 * Get the currently selected nodes.
14952 * @return {Array} An array of HTMLElements
14954 getSelectedNodes : function(){
14955 return this.selections;
14959 * Get the indexes of the selected nodes.
14962 getSelectedIndexes : function(){
14963 var indexes = [], s = this.selections;
14964 for(var i = 0, len = s.length; i < len; i++){
14965 indexes.push(s[i].nodeIndex);
14971 * Clear all selections
14972 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14974 clearSelections : function(suppressEvent){
14975 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14976 this.cmp.elements = this.selections;
14977 this.cmp.removeClass(this.selectedClass);
14978 this.selections = [];
14979 if(!suppressEvent){
14980 this.fireEvent("selectionchange", this, this.selections);
14986 * Returns true if the passed node is selected
14987 * @param {HTMLElement/Number} node The node or node index
14988 * @return {Boolean}
14990 isSelected : function(node){
14991 var s = this.selections;
14995 node = this.getNode(node);
14996 return s.indexOf(node) !== -1;
15001 * @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
15002 * @param {Boolean} keepExisting (optional) true to keep existing selections
15003 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15005 select : function(nodeInfo, keepExisting, suppressEvent){
15006 if(nodeInfo instanceof Array){
15008 this.clearSelections(true);
15010 for(var i = 0, len = nodeInfo.length; i < len; i++){
15011 this.select(nodeInfo[i], true, true);
15015 var node = this.getNode(nodeInfo);
15016 if(!node || this.isSelected(node)){
15017 return; // already selected.
15020 this.clearSelections(true);
15023 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15024 Roo.fly(node).addClass(this.selectedClass);
15025 this.selections.push(node);
15026 if(!suppressEvent){
15027 this.fireEvent("selectionchange", this, this.selections);
15035 * @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
15036 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15037 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15039 unselect : function(nodeInfo, keepExisting, suppressEvent)
15041 if(nodeInfo instanceof Array){
15042 Roo.each(this.selections, function(s) {
15043 this.unselect(s, nodeInfo);
15047 var node = this.getNode(nodeInfo);
15048 if(!node || !this.isSelected(node)){
15049 //Roo.log("not selected");
15050 return; // not selected.
15054 Roo.each(this.selections, function(s) {
15056 Roo.fly(node).removeClass(this.selectedClass);
15063 this.selections= ns;
15064 this.fireEvent("selectionchange", this, this.selections);
15068 * Gets a template node.
15069 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15070 * @return {HTMLElement} The node or null if it wasn't found
15072 getNode : function(nodeInfo){
15073 if(typeof nodeInfo == "string"){
15074 return document.getElementById(nodeInfo);
15075 }else if(typeof nodeInfo == "number"){
15076 return this.nodes[nodeInfo];
15082 * Gets a range template nodes.
15083 * @param {Number} startIndex
15084 * @param {Number} endIndex
15085 * @return {Array} An array of nodes
15087 getNodes : function(start, end){
15088 var ns = this.nodes;
15089 start = start || 0;
15090 end = typeof end == "undefined" ? ns.length - 1 : end;
15093 for(var i = start; i <= end; i++){
15097 for(var i = start; i >= end; i--){
15105 * Finds the index of the passed node
15106 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15107 * @return {Number} The index of the node or -1
15109 indexOf : function(node){
15110 node = this.getNode(node);
15111 if(typeof node.nodeIndex == "number"){
15112 return node.nodeIndex;
15114 var ns = this.nodes;
15115 for(var i = 0, len = ns.length; i < len; i++){
15126 * based on jquery fullcalendar
15130 Roo.bootstrap = Roo.bootstrap || {};
15132 * @class Roo.bootstrap.Calendar
15133 * @extends Roo.bootstrap.Component
15134 * Bootstrap Calendar class
15135 * @cfg {Boolean} loadMask (true|false) default false
15136 * @cfg {Object} header generate the user specific header of the calendar, default false
15139 * Create a new Container
15140 * @param {Object} config The config object
15145 Roo.bootstrap.Calendar = function(config){
15146 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15150 * Fires when a date is selected
15151 * @param {DatePicker} this
15152 * @param {Date} date The selected date
15156 * @event monthchange
15157 * Fires when the displayed month changes
15158 * @param {DatePicker} this
15159 * @param {Date} date The selected month
15161 'monthchange': true,
15163 * @event evententer
15164 * Fires when mouse over an event
15165 * @param {Calendar} this
15166 * @param {event} Event
15168 'evententer': true,
15170 * @event eventleave
15171 * Fires when the mouse leaves an
15172 * @param {Calendar} this
15175 'eventleave': true,
15177 * @event eventclick
15178 * Fires when the mouse click an
15179 * @param {Calendar} this
15188 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15191 * @cfg {Number} startDay
15192 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15200 getAutoCreate : function(){
15203 var fc_button = function(name, corner, style, content ) {
15204 return Roo.apply({},{
15206 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15208 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15211 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15222 style : 'width:100%',
15229 cls : 'fc-header-left',
15231 fc_button('prev', 'left', 'arrow', '‹' ),
15232 fc_button('next', 'right', 'arrow', '›' ),
15233 { tag: 'span', cls: 'fc-header-space' },
15234 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15242 cls : 'fc-header-center',
15246 cls: 'fc-header-title',
15249 html : 'month / year'
15257 cls : 'fc-header-right',
15259 /* fc_button('month', 'left', '', 'month' ),
15260 fc_button('week', '', '', 'week' ),
15261 fc_button('day', 'right', '', 'day' )
15273 header = this.header;
15276 var cal_heads = function() {
15278 // fixme - handle this.
15280 for (var i =0; i < Date.dayNames.length; i++) {
15281 var d = Date.dayNames[i];
15284 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15285 html : d.substring(0,3)
15289 ret[0].cls += ' fc-first';
15290 ret[6].cls += ' fc-last';
15293 var cal_cell = function(n) {
15296 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15301 cls: 'fc-day-number',
15305 cls: 'fc-day-content',
15309 style: 'position: relative;' // height: 17px;
15321 var cal_rows = function() {
15324 for (var r = 0; r < 6; r++) {
15331 for (var i =0; i < Date.dayNames.length; i++) {
15332 var d = Date.dayNames[i];
15333 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15336 row.cn[0].cls+=' fc-first';
15337 row.cn[0].cn[0].style = 'min-height:90px';
15338 row.cn[6].cls+=' fc-last';
15342 ret[0].cls += ' fc-first';
15343 ret[4].cls += ' fc-prev-last';
15344 ret[5].cls += ' fc-last';
15351 cls: 'fc-border-separate',
15352 style : 'width:100%',
15360 cls : 'fc-first fc-last',
15378 cls : 'fc-content',
15379 style : "position: relative;",
15382 cls : 'fc-view fc-view-month fc-grid',
15383 style : 'position: relative',
15384 unselectable : 'on',
15387 cls : 'fc-event-container',
15388 style : 'position:absolute;z-index:8;top:0;left:0;'
15406 initEvents : function()
15409 throw "can not find store for calendar";
15415 style: "text-align:center",
15419 style: "background-color:white;width:50%;margin:250 auto",
15423 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15434 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15436 var size = this.el.select('.fc-content', true).first().getSize();
15437 this.maskEl.setSize(size.width, size.height);
15438 this.maskEl.enableDisplayMode("block");
15439 if(!this.loadMask){
15440 this.maskEl.hide();
15443 this.store = Roo.factory(this.store, Roo.data);
15444 this.store.on('load', this.onLoad, this);
15445 this.store.on('beforeload', this.onBeforeLoad, this);
15449 this.cells = this.el.select('.fc-day',true);
15450 //Roo.log(this.cells);
15451 this.textNodes = this.el.query('.fc-day-number');
15452 this.cells.addClassOnOver('fc-state-hover');
15454 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15455 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15456 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15457 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15459 this.on('monthchange', this.onMonthChange, this);
15461 this.update(new Date().clearTime());
15464 resize : function() {
15465 var sz = this.el.getSize();
15467 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15468 this.el.select('.fc-day-content div',true).setHeight(34);
15473 showPrevMonth : function(e){
15474 this.update(this.activeDate.add("mo", -1));
15476 showToday : function(e){
15477 this.update(new Date().clearTime());
15480 showNextMonth : function(e){
15481 this.update(this.activeDate.add("mo", 1));
15485 showPrevYear : function(){
15486 this.update(this.activeDate.add("y", -1));
15490 showNextYear : function(){
15491 this.update(this.activeDate.add("y", 1));
15496 update : function(date)
15498 var vd = this.activeDate;
15499 this.activeDate = date;
15500 // if(vd && this.el){
15501 // var t = date.getTime();
15502 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15503 // Roo.log('using add remove');
15505 // this.fireEvent('monthchange', this, date);
15507 // this.cells.removeClass("fc-state-highlight");
15508 // this.cells.each(function(c){
15509 // if(c.dateValue == t){
15510 // c.addClass("fc-state-highlight");
15511 // setTimeout(function(){
15512 // try{c.dom.firstChild.focus();}catch(e){}
15522 var days = date.getDaysInMonth();
15524 var firstOfMonth = date.getFirstDateOfMonth();
15525 var startingPos = firstOfMonth.getDay()-this.startDay;
15527 if(startingPos < this.startDay){
15531 var pm = date.add(Date.MONTH, -1);
15532 var prevStart = pm.getDaysInMonth()-startingPos;
15534 this.cells = this.el.select('.fc-day',true);
15535 this.textNodes = this.el.query('.fc-day-number');
15536 this.cells.addClassOnOver('fc-state-hover');
15538 var cells = this.cells.elements;
15539 var textEls = this.textNodes;
15541 Roo.each(cells, function(cell){
15542 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15545 days += startingPos;
15547 // convert everything to numbers so it's fast
15548 var day = 86400000;
15549 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15552 //Roo.log(prevStart);
15554 var today = new Date().clearTime().getTime();
15555 var sel = date.clearTime().getTime();
15556 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15557 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15558 var ddMatch = this.disabledDatesRE;
15559 var ddText = this.disabledDatesText;
15560 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15561 var ddaysText = this.disabledDaysText;
15562 var format = this.format;
15564 var setCellClass = function(cal, cell){
15568 //Roo.log('set Cell Class');
15570 var t = d.getTime();
15574 cell.dateValue = t;
15576 cell.className += " fc-today";
15577 cell.className += " fc-state-highlight";
15578 cell.title = cal.todayText;
15581 // disable highlight in other month..
15582 //cell.className += " fc-state-highlight";
15587 cell.className = " fc-state-disabled";
15588 cell.title = cal.minText;
15592 cell.className = " fc-state-disabled";
15593 cell.title = cal.maxText;
15597 if(ddays.indexOf(d.getDay()) != -1){
15598 cell.title = ddaysText;
15599 cell.className = " fc-state-disabled";
15602 if(ddMatch && format){
15603 var fvalue = d.dateFormat(format);
15604 if(ddMatch.test(fvalue)){
15605 cell.title = ddText.replace("%0", fvalue);
15606 cell.className = " fc-state-disabled";
15610 if (!cell.initialClassName) {
15611 cell.initialClassName = cell.dom.className;
15614 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15619 for(; i < startingPos; i++) {
15620 textEls[i].innerHTML = (++prevStart);
15621 d.setDate(d.getDate()+1);
15623 cells[i].className = "fc-past fc-other-month";
15624 setCellClass(this, cells[i]);
15629 for(; i < days; i++){
15630 intDay = i - startingPos + 1;
15631 textEls[i].innerHTML = (intDay);
15632 d.setDate(d.getDate()+1);
15634 cells[i].className = ''; // "x-date-active";
15635 setCellClass(this, cells[i]);
15639 for(; i < 42; i++) {
15640 textEls[i].innerHTML = (++extraDays);
15641 d.setDate(d.getDate()+1);
15643 cells[i].className = "fc-future fc-other-month";
15644 setCellClass(this, cells[i]);
15647 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15649 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15651 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15652 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15654 if(totalRows != 6){
15655 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15656 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15659 this.fireEvent('monthchange', this, date);
15663 if(!this.internalRender){
15664 var main = this.el.dom.firstChild;
15665 var w = main.offsetWidth;
15666 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15667 Roo.fly(main).setWidth(w);
15668 this.internalRender = true;
15669 // opera does not respect the auto grow header center column
15670 // then, after it gets a width opera refuses to recalculate
15671 // without a second pass
15672 if(Roo.isOpera && !this.secondPass){
15673 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15674 this.secondPass = true;
15675 this.update.defer(10, this, [date]);
15682 findCell : function(dt) {
15683 dt = dt.clearTime().getTime();
15685 this.cells.each(function(c){
15686 //Roo.log("check " +c.dateValue + '?=' + dt);
15687 if(c.dateValue == dt){
15697 findCells : function(ev) {
15698 var s = ev.start.clone().clearTime().getTime();
15700 var e= ev.end.clone().clearTime().getTime();
15703 this.cells.each(function(c){
15704 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15706 if(c.dateValue > e){
15709 if(c.dateValue < s){
15718 // findBestRow: function(cells)
15722 // for (var i =0 ; i < cells.length;i++) {
15723 // ret = Math.max(cells[i].rows || 0,ret);
15730 addItem : function(ev)
15732 // look for vertical location slot in
15733 var cells = this.findCells(ev);
15735 // ev.row = this.findBestRow(cells);
15737 // work out the location.
15741 for(var i =0; i < cells.length; i++) {
15743 cells[i].row = cells[0].row;
15746 cells[i].row = cells[i].row + 1;
15756 if (crow.start.getY() == cells[i].getY()) {
15758 crow.end = cells[i];
15775 cells[0].events.push(ev);
15777 this.calevents.push(ev);
15780 clearEvents: function() {
15782 if(!this.calevents){
15786 Roo.each(this.cells.elements, function(c){
15792 Roo.each(this.calevents, function(e) {
15793 Roo.each(e.els, function(el) {
15794 el.un('mouseenter' ,this.onEventEnter, this);
15795 el.un('mouseleave' ,this.onEventLeave, this);
15800 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15806 renderEvents: function()
15810 this.cells.each(function(c) {
15819 if(c.row != c.events.length){
15820 r = 4 - (4 - (c.row - c.events.length));
15823 c.events = ev.slice(0, r);
15824 c.more = ev.slice(r);
15826 if(c.more.length && c.more.length == 1){
15827 c.events.push(c.more.pop());
15830 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15834 this.cells.each(function(c) {
15836 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15839 for (var e = 0; e < c.events.length; e++){
15840 var ev = c.events[e];
15841 var rows = ev.rows;
15843 for(var i = 0; i < rows.length; i++) {
15845 // how many rows should it span..
15848 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15849 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15851 unselectable : "on",
15854 cls: 'fc-event-inner',
15858 // cls: 'fc-event-time',
15859 // html : cells.length > 1 ? '' : ev.time
15863 cls: 'fc-event-title',
15864 html : String.format('{0}', ev.title)
15871 cls: 'ui-resizable-handle ui-resizable-e',
15872 html : '  '
15879 cfg.cls += ' fc-event-start';
15881 if ((i+1) == rows.length) {
15882 cfg.cls += ' fc-event-end';
15885 var ctr = _this.el.select('.fc-event-container',true).first();
15886 var cg = ctr.createChild(cfg);
15888 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15889 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15891 var r = (c.more.length) ? 1 : 0;
15892 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15893 cg.setWidth(ebox.right - sbox.x -2);
15895 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15896 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15897 cg.on('click', _this.onEventClick, _this, ev);
15908 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15909 style : 'position: absolute',
15910 unselectable : "on",
15913 cls: 'fc-event-inner',
15917 cls: 'fc-event-title',
15925 cls: 'ui-resizable-handle ui-resizable-e',
15926 html : '  '
15932 var ctr = _this.el.select('.fc-event-container',true).first();
15933 var cg = ctr.createChild(cfg);
15935 var sbox = c.select('.fc-day-content',true).first().getBox();
15936 var ebox = c.select('.fc-day-content',true).first().getBox();
15938 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15939 cg.setWidth(ebox.right - sbox.x -2);
15941 cg.on('click', _this.onMoreEventClick, _this, c.more);
15951 onEventEnter: function (e, el,event,d) {
15952 this.fireEvent('evententer', this, el, event);
15955 onEventLeave: function (e, el,event,d) {
15956 this.fireEvent('eventleave', this, el, event);
15959 onEventClick: function (e, el,event,d) {
15960 this.fireEvent('eventclick', this, el, event);
15963 onMonthChange: function () {
15967 onMoreEventClick: function(e, el, more)
15971 this.calpopover.placement = 'right';
15972 this.calpopover.setTitle('More');
15974 this.calpopover.setContent('');
15976 var ctr = this.calpopover.el.select('.popover-content', true).first();
15978 Roo.each(more, function(m){
15980 cls : 'fc-event-hori fc-event-draggable',
15983 var cg = ctr.createChild(cfg);
15985 cg.on('click', _this.onEventClick, _this, m);
15988 this.calpopover.show(el);
15993 onLoad: function ()
15995 this.calevents = [];
15998 if(this.store.getCount() > 0){
15999 this.store.data.each(function(d){
16002 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16003 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16004 time : d.data.start_time,
16005 title : d.data.title,
16006 description : d.data.description,
16007 venue : d.data.venue
16012 this.renderEvents();
16014 if(this.calevents.length && this.loadMask){
16015 this.maskEl.hide();
16019 onBeforeLoad: function()
16021 this.clearEvents();
16023 this.maskEl.show();
16037 * @class Roo.bootstrap.Popover
16038 * @extends Roo.bootstrap.Component
16039 * Bootstrap Popover class
16040 * @cfg {String} html contents of the popover (or false to use children..)
16041 * @cfg {String} title of popover (or false to hide)
16042 * @cfg {String} placement how it is placed
16043 * @cfg {String} trigger click || hover (or false to trigger manually)
16044 * @cfg {String} over what (parent or false to trigger manually.)
16045 * @cfg {Number} delay - delay before showing
16048 * Create a new Popover
16049 * @param {Object} config The config object
16052 Roo.bootstrap.Popover = function(config){
16053 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16059 * After the popover show
16061 * @param {Roo.bootstrap.Popover} this
16066 * After the popover hide
16068 * @param {Roo.bootstrap.Popover} this
16074 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16076 title: 'Fill in a title',
16079 placement : 'right',
16080 trigger : 'hover', // hover
16086 can_build_overlaid : false,
16088 getChildContainer : function()
16090 return this.el.select('.popover-content',true).first();
16093 getAutoCreate : function(){
16096 cls : 'popover roo-dynamic',
16097 style: 'display:block',
16103 cls : 'popover-inner',
16107 cls: 'popover-title',
16111 cls : 'popover-content',
16122 setTitle: function(str)
16125 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16127 setContent: function(str)
16130 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16132 // as it get's added to the bottom of the page.
16133 onRender : function(ct, position)
16135 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16137 var cfg = Roo.apply({}, this.getAutoCreate());
16141 cfg.cls += ' ' + this.cls;
16144 cfg.style = this.style;
16146 //Roo.log("adding to ");
16147 this.el = Roo.get(document.body).createChild(cfg, position);
16148 // Roo.log(this.el);
16153 initEvents : function()
16155 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16156 this.el.enableDisplayMode('block');
16158 if (this.over === false) {
16161 if (this.triggers === false) {
16164 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16165 var triggers = this.trigger ? this.trigger.split(' ') : [];
16166 Roo.each(triggers, function(trigger) {
16168 if (trigger == 'click') {
16169 on_el.on('click', this.toggle, this);
16170 } else if (trigger != 'manual') {
16171 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16172 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16174 on_el.on(eventIn ,this.enter, this);
16175 on_el.on(eventOut, this.leave, this);
16186 toggle : function () {
16187 this.hoverState == 'in' ? this.leave() : this.enter();
16190 enter : function () {
16192 clearTimeout(this.timeout);
16194 this.hoverState = 'in';
16196 if (!this.delay || !this.delay.show) {
16201 this.timeout = setTimeout(function () {
16202 if (_t.hoverState == 'in') {
16205 }, this.delay.show)
16208 leave : function() {
16209 clearTimeout(this.timeout);
16211 this.hoverState = 'out';
16213 if (!this.delay || !this.delay.hide) {
16218 this.timeout = setTimeout(function () {
16219 if (_t.hoverState == 'out') {
16222 }, this.delay.hide)
16225 show : function (on_el)
16228 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16232 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16233 if (this.html !== false) {
16234 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16236 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16237 if (!this.title.length) {
16238 this.el.select('.popover-title',true).hide();
16241 var placement = typeof this.placement == 'function' ?
16242 this.placement.call(this, this.el, on_el) :
16245 var autoToken = /\s?auto?\s?/i;
16246 var autoPlace = autoToken.test(placement);
16248 placement = placement.replace(autoToken, '') || 'top';
16252 //this.el.setXY([0,0]);
16254 this.el.dom.style.display='block';
16255 this.el.addClass(placement);
16257 //this.el.appendTo(on_el);
16259 var p = this.getPosition();
16260 var box = this.el.getBox();
16265 var align = Roo.bootstrap.Popover.alignment[placement];
16266 this.el.alignTo(on_el, align[0],align[1]);
16267 //var arrow = this.el.select('.arrow',true).first();
16268 //arrow.set(align[2],
16270 this.el.addClass('in');
16273 if (this.el.hasClass('fade')) {
16277 this.hoverState = 'in';
16279 this.fireEvent('show', this);
16284 this.el.setXY([0,0]);
16285 this.el.removeClass('in');
16287 this.hoverState = null;
16289 this.fireEvent('hide', this);
16294 Roo.bootstrap.Popover.alignment = {
16295 'left' : ['r-l', [-10,0], 'right'],
16296 'right' : ['l-r', [10,0], 'left'],
16297 'bottom' : ['t-b', [0,10], 'top'],
16298 'top' : [ 'b-t', [0,-10], 'bottom']
16309 * @class Roo.bootstrap.Progress
16310 * @extends Roo.bootstrap.Component
16311 * Bootstrap Progress class
16312 * @cfg {Boolean} striped striped of the progress bar
16313 * @cfg {Boolean} active animated of the progress bar
16317 * Create a new Progress
16318 * @param {Object} config The config object
16321 Roo.bootstrap.Progress = function(config){
16322 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16325 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16330 getAutoCreate : function(){
16338 cfg.cls += ' progress-striped';
16342 cfg.cls += ' active';
16361 * @class Roo.bootstrap.ProgressBar
16362 * @extends Roo.bootstrap.Component
16363 * Bootstrap ProgressBar class
16364 * @cfg {Number} aria_valuenow aria-value now
16365 * @cfg {Number} aria_valuemin aria-value min
16366 * @cfg {Number} aria_valuemax aria-value max
16367 * @cfg {String} label label for the progress bar
16368 * @cfg {String} panel (success | info | warning | danger )
16369 * @cfg {String} role role of the progress bar
16370 * @cfg {String} sr_only text
16374 * Create a new ProgressBar
16375 * @param {Object} config The config object
16378 Roo.bootstrap.ProgressBar = function(config){
16379 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16382 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16386 aria_valuemax : 100,
16392 getAutoCreate : function()
16397 cls: 'progress-bar',
16398 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16410 cfg.role = this.role;
16413 if(this.aria_valuenow){
16414 cfg['aria-valuenow'] = this.aria_valuenow;
16417 if(this.aria_valuemin){
16418 cfg['aria-valuemin'] = this.aria_valuemin;
16421 if(this.aria_valuemax){
16422 cfg['aria-valuemax'] = this.aria_valuemax;
16425 if(this.label && !this.sr_only){
16426 cfg.html = this.label;
16430 cfg.cls += ' progress-bar-' + this.panel;
16436 update : function(aria_valuenow)
16438 this.aria_valuenow = aria_valuenow;
16440 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16455 * @class Roo.bootstrap.TabGroup
16456 * @extends Roo.bootstrap.Column
16457 * Bootstrap Column class
16458 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16459 * @cfg {Boolean} carousel true to make the group behave like a carousel
16460 * @cfg {Boolean} bullets show bullets for the panels
16461 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16462 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16463 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16464 * @cfg {Boolean} showarrow (true|false) show arrow default true
16467 * Create a new TabGroup
16468 * @param {Object} config The config object
16471 Roo.bootstrap.TabGroup = function(config){
16472 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16474 this.navId = Roo.id();
16477 Roo.bootstrap.TabGroup.register(this);
16481 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16484 transition : false,
16489 slideOnTouch : false,
16492 getAutoCreate : function()
16494 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16496 cfg.cls += ' tab-content';
16498 if (this.carousel) {
16499 cfg.cls += ' carousel slide';
16502 cls : 'carousel-inner',
16506 if(this.bullets && !Roo.isTouch){
16509 cls : 'carousel-bullets',
16513 if(this.bullets_cls){
16514 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16521 cfg.cn[0].cn.push(bullets);
16524 if(this.showarrow){
16525 cfg.cn[0].cn.push({
16527 class : 'carousel-arrow',
16531 class : 'carousel-prev',
16535 class : 'fa fa-chevron-left'
16541 class : 'carousel-next',
16545 class : 'fa fa-chevron-right'
16558 initEvents: function()
16560 if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16561 this.el.on("touchstart", this.onTouchStart, this);
16564 if(this.autoslide){
16567 this.slideFn = window.setInterval(function() {
16568 _this.showPanelNext();
16572 if(this.showarrow){
16573 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16574 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16580 onTouchStart : function(e, el, o)
16582 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16586 this.showPanelNext();
16589 getChildContainer : function()
16591 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16595 * register a Navigation item
16596 * @param {Roo.bootstrap.NavItem} the navitem to add
16598 register : function(item)
16600 this.tabs.push( item);
16601 item.navId = this.navId; // not really needed..
16606 getActivePanel : function()
16609 Roo.each(this.tabs, function(t) {
16619 getPanelByName : function(n)
16622 Roo.each(this.tabs, function(t) {
16623 if (t.tabId == n) {
16631 indexOfPanel : function(p)
16634 Roo.each(this.tabs, function(t,i) {
16635 if (t.tabId == p.tabId) {
16644 * show a specific panel
16645 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16646 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16648 showPanel : function (pan)
16650 if(this.transition || typeof(pan) == 'undefined'){
16651 Roo.log("waiting for the transitionend");
16655 if (typeof(pan) == 'number') {
16656 pan = this.tabs[pan];
16659 if (typeof(pan) == 'string') {
16660 pan = this.getPanelByName(pan);
16663 var cur = this.getActivePanel();
16666 Roo.log('pan or acitve pan is undefined');
16670 if (pan.tabId == this.getActivePanel().tabId) {
16674 if (false === cur.fireEvent('beforedeactivate')) {
16678 if(this.bullets > 0 && !Roo.isTouch){
16679 this.setActiveBullet(this.indexOfPanel(pan));
16682 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16684 this.transition = true;
16685 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16686 var lr = dir == 'next' ? 'left' : 'right';
16687 pan.el.addClass(dir); // or prev
16688 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16689 cur.el.addClass(lr); // or right
16690 pan.el.addClass(lr);
16693 cur.el.on('transitionend', function() {
16694 Roo.log("trans end?");
16696 pan.el.removeClass([lr,dir]);
16697 pan.setActive(true);
16699 cur.el.removeClass([lr]);
16700 cur.setActive(false);
16702 _this.transition = false;
16704 }, this, { single: true } );
16709 cur.setActive(false);
16710 pan.setActive(true);
16715 showPanelNext : function()
16717 var i = this.indexOfPanel(this.getActivePanel());
16719 if (i >= this.tabs.length - 1 && !this.autoslide) {
16723 if (i >= this.tabs.length - 1 && this.autoslide) {
16727 this.showPanel(this.tabs[i+1]);
16730 showPanelPrev : function()
16732 var i = this.indexOfPanel(this.getActivePanel());
16734 if (i < 1 && !this.autoslide) {
16738 if (i < 1 && this.autoslide) {
16739 i = this.tabs.length;
16742 this.showPanel(this.tabs[i-1]);
16746 addBullet: function()
16748 if(!this.bullets || Roo.isTouch){
16751 var ctr = this.el.select('.carousel-bullets',true).first();
16752 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16753 var bullet = ctr.createChild({
16754 cls : 'bullet bullet-' + i
16755 },ctr.dom.lastChild);
16760 bullet.on('click', (function(e, el, o, ii, t){
16762 e.preventDefault();
16764 this.showPanel(ii);
16766 if(this.autoslide && this.slideFn){
16767 clearInterval(this.slideFn);
16768 this.slideFn = window.setInterval(function() {
16769 _this.showPanelNext();
16773 }).createDelegate(this, [i, bullet], true));
16778 setActiveBullet : function(i)
16784 Roo.each(this.el.select('.bullet', true).elements, function(el){
16785 el.removeClass('selected');
16788 var bullet = this.el.select('.bullet-' + i, true).first();
16794 bullet.addClass('selected');
16805 Roo.apply(Roo.bootstrap.TabGroup, {
16809 * register a Navigation Group
16810 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16812 register : function(navgrp)
16814 this.groups[navgrp.navId] = navgrp;
16818 * fetch a Navigation Group based on the navigation ID
16819 * if one does not exist , it will get created.
16820 * @param {string} the navgroup to add
16821 * @returns {Roo.bootstrap.NavGroup} the navgroup
16823 get: function(navId) {
16824 if (typeof(this.groups[navId]) == 'undefined') {
16825 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16827 return this.groups[navId] ;
16842 * @class Roo.bootstrap.TabPanel
16843 * @extends Roo.bootstrap.Component
16844 * Bootstrap TabPanel class
16845 * @cfg {Boolean} active panel active
16846 * @cfg {String} html panel content
16847 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16848 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16849 * @cfg {String} href click to link..
16853 * Create a new TabPanel
16854 * @param {Object} config The config object
16857 Roo.bootstrap.TabPanel = function(config){
16858 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16862 * Fires when the active status changes
16863 * @param {Roo.bootstrap.TabPanel} this
16864 * @param {Boolean} state the new state
16869 * @event beforedeactivate
16870 * Fires before a tab is de-activated - can be used to do validation on a form.
16871 * @param {Roo.bootstrap.TabPanel} this
16872 * @return {Boolean} false if there is an error
16875 'beforedeactivate': true
16878 this.tabId = this.tabId || Roo.id();
16882 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16890 getAutoCreate : function(){
16893 // item is needed for carousel - not sure if it has any effect otherwise
16894 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
16895 html: this.html || ''
16899 cfg.cls += ' active';
16903 cfg.tabId = this.tabId;
16910 initEvents: function()
16912 var p = this.parent();
16913 this.navId = this.navId || p.navId;
16915 if (typeof(this.navId) != 'undefined') {
16916 // not really needed.. but just in case.. parent should be a NavGroup.
16917 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16921 var i = tg.tabs.length - 1;
16923 if(this.active && tg.bullets > 0 && i < tg.bullets){
16924 tg.setActiveBullet(i);
16928 if(this.href.length){
16929 this.el.on('click', this.onClick, this);
16934 onRender : function(ct, position)
16936 // Roo.log("Call onRender: " + this.xtype);
16938 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16946 setActive: function(state)
16948 Roo.log("panel - set active " + this.tabId + "=" + state);
16950 this.active = state;
16952 this.el.removeClass('active');
16954 } else if (!this.el.hasClass('active')) {
16955 this.el.addClass('active');
16958 this.fireEvent('changed', this, state);
16961 onClick: function(e)
16963 e.preventDefault();
16965 window.location.href = this.href;
16982 * @class Roo.bootstrap.DateField
16983 * @extends Roo.bootstrap.Input
16984 * Bootstrap DateField class
16985 * @cfg {Number} weekStart default 0
16986 * @cfg {String} viewMode default empty, (months|years)
16987 * @cfg {String} minViewMode default empty, (months|years)
16988 * @cfg {Number} startDate default -Infinity
16989 * @cfg {Number} endDate default Infinity
16990 * @cfg {Boolean} todayHighlight default false
16991 * @cfg {Boolean} todayBtn default false
16992 * @cfg {Boolean} calendarWeeks default false
16993 * @cfg {Object} daysOfWeekDisabled default empty
16994 * @cfg {Boolean} singleMode default false (true | false)
16996 * @cfg {Boolean} keyboardNavigation default true
16997 * @cfg {String} language default en
17000 * Create a new DateField
17001 * @param {Object} config The config object
17004 Roo.bootstrap.DateField = function(config){
17005 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17009 * Fires when this field show.
17010 * @param {Roo.bootstrap.DateField} this
17011 * @param {Mixed} date The date value
17016 * Fires when this field hide.
17017 * @param {Roo.bootstrap.DateField} this
17018 * @param {Mixed} date The date value
17023 * Fires when select a date.
17024 * @param {Roo.bootstrap.DateField} this
17025 * @param {Mixed} date The date value
17029 * @event beforeselect
17030 * Fires when before select a date.
17031 * @param {Roo.bootstrap.DateField} this
17032 * @param {Mixed} date The date value
17034 beforeselect : true
17038 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17041 * @cfg {String} format
17042 * The default date format string which can be overriden for localization support. The format must be
17043 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17047 * @cfg {String} altFormats
17048 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17049 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17051 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17059 todayHighlight : false,
17065 keyboardNavigation: true,
17067 calendarWeeks: false,
17069 startDate: -Infinity,
17073 daysOfWeekDisabled: [],
17077 singleMode : false,
17079 UTCDate: function()
17081 return new Date(Date.UTC.apply(Date, arguments));
17084 UTCToday: function()
17086 var today = new Date();
17087 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17090 getDate: function() {
17091 var d = this.getUTCDate();
17092 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17095 getUTCDate: function() {
17099 setDate: function(d) {
17100 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17103 setUTCDate: function(d) {
17105 this.setValue(this.formatDate(this.date));
17108 onRender: function(ct, position)
17111 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17113 this.language = this.language || 'en';
17114 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17115 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17117 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17118 this.format = this.format || 'm/d/y';
17119 this.isInline = false;
17120 this.isInput = true;
17121 this.component = this.el.select('.add-on', true).first() || false;
17122 this.component = (this.component && this.component.length === 0) ? false : this.component;
17123 this.hasInput = this.component && this.inputEL().length;
17125 if (typeof(this.minViewMode === 'string')) {
17126 switch (this.minViewMode) {
17128 this.minViewMode = 1;
17131 this.minViewMode = 2;
17134 this.minViewMode = 0;
17139 if (typeof(this.viewMode === 'string')) {
17140 switch (this.viewMode) {
17153 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17155 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17157 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17159 this.picker().on('mousedown', this.onMousedown, this);
17160 this.picker().on('click', this.onClick, this);
17162 this.picker().addClass('datepicker-dropdown');
17164 this.startViewMode = this.viewMode;
17166 if(this.singleMode){
17167 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17168 v.setVisibilityMode(Roo.Element.DISPLAY);
17172 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17173 v.setStyle('width', '189px');
17177 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17178 if(!this.calendarWeeks){
17183 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17184 v.attr('colspan', function(i, val){
17185 return parseInt(val) + 1;
17190 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17192 this.setStartDate(this.startDate);
17193 this.setEndDate(this.endDate);
17195 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17202 if(this.isInline) {
17207 picker : function()
17209 return this.pickerEl;
17210 // return this.el.select('.datepicker', true).first();
17213 fillDow: function()
17215 var dowCnt = this.weekStart;
17224 if(this.calendarWeeks){
17232 while (dowCnt < this.weekStart + 7) {
17236 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17240 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17243 fillMonths: function()
17246 var months = this.picker().select('>.datepicker-months td', true).first();
17248 months.dom.innerHTML = '';
17254 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17257 months.createChild(month);
17264 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;
17266 if (this.date < this.startDate) {
17267 this.viewDate = new Date(this.startDate);
17268 } else if (this.date > this.endDate) {
17269 this.viewDate = new Date(this.endDate);
17271 this.viewDate = new Date(this.date);
17279 var d = new Date(this.viewDate),
17280 year = d.getUTCFullYear(),
17281 month = d.getUTCMonth(),
17282 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17283 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17284 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17285 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17286 currentDate = this.date && this.date.valueOf(),
17287 today = this.UTCToday();
17289 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17291 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17293 // this.picker.select('>tfoot th.today').
17294 // .text(dates[this.language].today)
17295 // .toggle(this.todayBtn !== false);
17297 this.updateNavArrows();
17300 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17302 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17304 prevMonth.setUTCDate(day);
17306 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17308 var nextMonth = new Date(prevMonth);
17310 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17312 nextMonth = nextMonth.valueOf();
17314 var fillMonths = false;
17316 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17318 while(prevMonth.valueOf() < nextMonth) {
17321 if (prevMonth.getUTCDay() === this.weekStart) {
17323 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17331 if(this.calendarWeeks){
17332 // ISO 8601: First week contains first thursday.
17333 // ISO also states week starts on Monday, but we can be more abstract here.
17335 // Start of current week: based on weekstart/current date
17336 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17337 // Thursday of this week
17338 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17339 // First Thursday of year, year from thursday
17340 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17341 // Calendar week: ms between thursdays, div ms per day, div 7 days
17342 calWeek = (th - yth) / 864e5 / 7 + 1;
17344 fillMonths.cn.push({
17352 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17354 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17357 if (this.todayHighlight &&
17358 prevMonth.getUTCFullYear() == today.getFullYear() &&
17359 prevMonth.getUTCMonth() == today.getMonth() &&
17360 prevMonth.getUTCDate() == today.getDate()) {
17361 clsName += ' today';
17364 if (currentDate && prevMonth.valueOf() === currentDate) {
17365 clsName += ' active';
17368 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17369 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17370 clsName += ' disabled';
17373 fillMonths.cn.push({
17375 cls: 'day ' + clsName,
17376 html: prevMonth.getDate()
17379 prevMonth.setDate(prevMonth.getDate()+1);
17382 var currentYear = this.date && this.date.getUTCFullYear();
17383 var currentMonth = this.date && this.date.getUTCMonth();
17385 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17387 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17388 v.removeClass('active');
17390 if(currentYear === year && k === currentMonth){
17391 v.addClass('active');
17394 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17395 v.addClass('disabled');
17401 year = parseInt(year/10, 10) * 10;
17403 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17405 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17408 for (var i = -1; i < 11; i++) {
17409 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17411 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17419 showMode: function(dir)
17422 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17425 Roo.each(this.picker().select('>div',true).elements, function(v){
17426 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17429 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17434 if(this.isInline) {
17438 this.picker().removeClass(['bottom', 'top']);
17440 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17442 * place to the top of element!
17446 this.picker().addClass('top');
17447 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17452 this.picker().addClass('bottom');
17454 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17457 parseDate : function(value)
17459 if(!value || value instanceof Date){
17462 var v = Date.parseDate(value, this.format);
17463 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17464 v = Date.parseDate(value, 'Y-m-d');
17466 if(!v && this.altFormats){
17467 if(!this.altFormatsArray){
17468 this.altFormatsArray = this.altFormats.split("|");
17470 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17471 v = Date.parseDate(value, this.altFormatsArray[i]);
17477 formatDate : function(date, fmt)
17479 return (!date || !(date instanceof Date)) ?
17480 date : date.dateFormat(fmt || this.format);
17483 onFocus : function()
17485 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17489 onBlur : function()
17491 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17493 var d = this.inputEl().getValue();
17502 this.picker().show();
17506 this.fireEvent('show', this, this.date);
17511 if(this.isInline) {
17514 this.picker().hide();
17515 this.viewMode = this.startViewMode;
17518 this.fireEvent('hide', this, this.date);
17522 onMousedown: function(e)
17524 e.stopPropagation();
17525 e.preventDefault();
17530 Roo.bootstrap.DateField.superclass.keyup.call(this);
17534 setValue: function(v)
17536 if(this.fireEvent('beforeselect', this, v) !== false){
17537 var d = new Date(this.parseDate(v) ).clearTime();
17539 if(isNaN(d.getTime())){
17540 this.date = this.viewDate = '';
17541 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17545 v = this.formatDate(d);
17547 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17549 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17553 this.fireEvent('select', this, this.date);
17557 getValue: function()
17559 return this.formatDate(this.date);
17562 fireKey: function(e)
17564 if (!this.picker().isVisible()){
17565 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17571 var dateChanged = false,
17573 newDate, newViewDate;
17578 e.preventDefault();
17582 if (!this.keyboardNavigation) {
17585 dir = e.keyCode == 37 ? -1 : 1;
17588 newDate = this.moveYear(this.date, dir);
17589 newViewDate = this.moveYear(this.viewDate, dir);
17590 } else if (e.shiftKey){
17591 newDate = this.moveMonth(this.date, dir);
17592 newViewDate = this.moveMonth(this.viewDate, dir);
17594 newDate = new Date(this.date);
17595 newDate.setUTCDate(this.date.getUTCDate() + dir);
17596 newViewDate = new Date(this.viewDate);
17597 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17599 if (this.dateWithinRange(newDate)){
17600 this.date = newDate;
17601 this.viewDate = newViewDate;
17602 this.setValue(this.formatDate(this.date));
17604 e.preventDefault();
17605 dateChanged = true;
17610 if (!this.keyboardNavigation) {
17613 dir = e.keyCode == 38 ? -1 : 1;
17615 newDate = this.moveYear(this.date, dir);
17616 newViewDate = this.moveYear(this.viewDate, dir);
17617 } else if (e.shiftKey){
17618 newDate = this.moveMonth(this.date, dir);
17619 newViewDate = this.moveMonth(this.viewDate, dir);
17621 newDate = new Date(this.date);
17622 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17623 newViewDate = new Date(this.viewDate);
17624 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17626 if (this.dateWithinRange(newDate)){
17627 this.date = newDate;
17628 this.viewDate = newViewDate;
17629 this.setValue(this.formatDate(this.date));
17631 e.preventDefault();
17632 dateChanged = true;
17636 this.setValue(this.formatDate(this.date));
17638 e.preventDefault();
17641 this.setValue(this.formatDate(this.date));
17655 onClick: function(e)
17657 e.stopPropagation();
17658 e.preventDefault();
17660 var target = e.getTarget();
17662 if(target.nodeName.toLowerCase() === 'i'){
17663 target = Roo.get(target).dom.parentNode;
17666 var nodeName = target.nodeName;
17667 var className = target.className;
17668 var html = target.innerHTML;
17669 //Roo.log(nodeName);
17671 switch(nodeName.toLowerCase()) {
17673 switch(className) {
17679 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17680 switch(this.viewMode){
17682 this.viewDate = this.moveMonth(this.viewDate, dir);
17686 this.viewDate = this.moveYear(this.viewDate, dir);
17692 var date = new Date();
17693 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17695 this.setValue(this.formatDate(this.date));
17702 if (className.indexOf('disabled') < 0) {
17703 this.viewDate.setUTCDate(1);
17704 if (className.indexOf('month') > -1) {
17705 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17707 var year = parseInt(html, 10) || 0;
17708 this.viewDate.setUTCFullYear(year);
17712 if(this.singleMode){
17713 this.setValue(this.formatDate(this.viewDate));
17724 //Roo.log(className);
17725 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17726 var day = parseInt(html, 10) || 1;
17727 var year = this.viewDate.getUTCFullYear(),
17728 month = this.viewDate.getUTCMonth();
17730 if (className.indexOf('old') > -1) {
17737 } else if (className.indexOf('new') > -1) {
17745 //Roo.log([year,month,day]);
17746 this.date = this.UTCDate(year, month, day,0,0,0,0);
17747 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17749 //Roo.log(this.formatDate(this.date));
17750 this.setValue(this.formatDate(this.date));
17757 setStartDate: function(startDate)
17759 this.startDate = startDate || -Infinity;
17760 if (this.startDate !== -Infinity) {
17761 this.startDate = this.parseDate(this.startDate);
17764 this.updateNavArrows();
17767 setEndDate: function(endDate)
17769 this.endDate = endDate || Infinity;
17770 if (this.endDate !== Infinity) {
17771 this.endDate = this.parseDate(this.endDate);
17774 this.updateNavArrows();
17777 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17779 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17780 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17781 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17783 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17784 return parseInt(d, 10);
17787 this.updateNavArrows();
17790 updateNavArrows: function()
17792 if(this.singleMode){
17796 var d = new Date(this.viewDate),
17797 year = d.getUTCFullYear(),
17798 month = d.getUTCMonth();
17800 Roo.each(this.picker().select('.prev', true).elements, function(v){
17802 switch (this.viewMode) {
17805 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17811 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17818 Roo.each(this.picker().select('.next', true).elements, function(v){
17820 switch (this.viewMode) {
17823 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17829 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17837 moveMonth: function(date, dir)
17842 var new_date = new Date(date.valueOf()),
17843 day = new_date.getUTCDate(),
17844 month = new_date.getUTCMonth(),
17845 mag = Math.abs(dir),
17847 dir = dir > 0 ? 1 : -1;
17850 // If going back one month, make sure month is not current month
17851 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17853 return new_date.getUTCMonth() == month;
17855 // If going forward one month, make sure month is as expected
17856 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17858 return new_date.getUTCMonth() != new_month;
17860 new_month = month + dir;
17861 new_date.setUTCMonth(new_month);
17862 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17863 if (new_month < 0 || new_month > 11) {
17864 new_month = (new_month + 12) % 12;
17867 // For magnitudes >1, move one month at a time...
17868 for (var i=0; i<mag; i++) {
17869 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17870 new_date = this.moveMonth(new_date, dir);
17872 // ...then reset the day, keeping it in the new month
17873 new_month = new_date.getUTCMonth();
17874 new_date.setUTCDate(day);
17876 return new_month != new_date.getUTCMonth();
17879 // Common date-resetting loop -- if date is beyond end of month, make it
17882 new_date.setUTCDate(--day);
17883 new_date.setUTCMonth(new_month);
17888 moveYear: function(date, dir)
17890 return this.moveMonth(date, dir*12);
17893 dateWithinRange: function(date)
17895 return date >= this.startDate && date <= this.endDate;
17901 this.picker().remove();
17906 Roo.apply(Roo.bootstrap.DateField, {
17917 html: '<i class="fa fa-arrow-left"/>'
17927 html: '<i class="fa fa-arrow-right"/>'
17969 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17970 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17971 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17972 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17973 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17986 navFnc: 'FullYear',
17991 navFnc: 'FullYear',
17996 Roo.apply(Roo.bootstrap.DateField, {
18000 cls: 'datepicker dropdown-menu roo-dynamic',
18004 cls: 'datepicker-days',
18008 cls: 'table-condensed',
18010 Roo.bootstrap.DateField.head,
18014 Roo.bootstrap.DateField.footer
18021 cls: 'datepicker-months',
18025 cls: 'table-condensed',
18027 Roo.bootstrap.DateField.head,
18028 Roo.bootstrap.DateField.content,
18029 Roo.bootstrap.DateField.footer
18036 cls: 'datepicker-years',
18040 cls: 'table-condensed',
18042 Roo.bootstrap.DateField.head,
18043 Roo.bootstrap.DateField.content,
18044 Roo.bootstrap.DateField.footer
18063 * @class Roo.bootstrap.TimeField
18064 * @extends Roo.bootstrap.Input
18065 * Bootstrap DateField class
18069 * Create a new TimeField
18070 * @param {Object} config The config object
18073 Roo.bootstrap.TimeField = function(config){
18074 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18078 * Fires when this field show.
18079 * @param {Roo.bootstrap.DateField} thisthis
18080 * @param {Mixed} date The date value
18085 * Fires when this field hide.
18086 * @param {Roo.bootstrap.DateField} this
18087 * @param {Mixed} date The date value
18092 * Fires when select a date.
18093 * @param {Roo.bootstrap.DateField} this
18094 * @param {Mixed} date The date value
18100 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18103 * @cfg {String} format
18104 * The default time format string which can be overriden for localization support. The format must be
18105 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18109 onRender: function(ct, position)
18112 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18114 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18116 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18118 this.pop = this.picker().select('>.datepicker-time',true).first();
18119 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18121 this.picker().on('mousedown', this.onMousedown, this);
18122 this.picker().on('click', this.onClick, this);
18124 this.picker().addClass('datepicker-dropdown');
18129 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18130 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18131 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18132 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18133 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18134 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18138 fireKey: function(e){
18139 if (!this.picker().isVisible()){
18140 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18146 e.preventDefault();
18154 this.onTogglePeriod();
18157 this.onIncrementMinutes();
18160 this.onDecrementMinutes();
18169 onClick: function(e) {
18170 e.stopPropagation();
18171 e.preventDefault();
18174 picker : function()
18176 return this.el.select('.datepicker', true).first();
18179 fillTime: function()
18181 var time = this.pop.select('tbody', true).first();
18183 time.dom.innerHTML = '';
18198 cls: 'hours-up glyphicon glyphicon-chevron-up'
18218 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18239 cls: 'timepicker-hour',
18254 cls: 'timepicker-minute',
18269 cls: 'btn btn-primary period',
18291 cls: 'hours-down glyphicon glyphicon-chevron-down'
18311 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18329 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18336 var hours = this.time.getHours();
18337 var minutes = this.time.getMinutes();
18350 hours = hours - 12;
18354 hours = '0' + hours;
18358 minutes = '0' + minutes;
18361 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18362 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18363 this.pop.select('button', true).first().dom.innerHTML = period;
18369 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18371 var cls = ['bottom'];
18373 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18380 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18385 this.picker().addClass(cls.join('-'));
18389 Roo.each(cls, function(c){
18391 _this.picker().setTop(_this.inputEl().getHeight());
18395 _this.picker().setTop(0 - _this.picker().getHeight());
18400 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18404 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18411 onFocus : function()
18413 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18417 onBlur : function()
18419 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18425 this.picker().show();
18430 this.fireEvent('show', this, this.date);
18435 this.picker().hide();
18438 this.fireEvent('hide', this, this.date);
18441 setTime : function()
18444 this.setValue(this.time.format(this.format));
18446 this.fireEvent('select', this, this.date);
18451 onMousedown: function(e){
18452 e.stopPropagation();
18453 e.preventDefault();
18456 onIncrementHours: function()
18458 Roo.log('onIncrementHours');
18459 this.time = this.time.add(Date.HOUR, 1);
18464 onDecrementHours: function()
18466 Roo.log('onDecrementHours');
18467 this.time = this.time.add(Date.HOUR, -1);
18471 onIncrementMinutes: function()
18473 Roo.log('onIncrementMinutes');
18474 this.time = this.time.add(Date.MINUTE, 1);
18478 onDecrementMinutes: function()
18480 Roo.log('onDecrementMinutes');
18481 this.time = this.time.add(Date.MINUTE, -1);
18485 onTogglePeriod: function()
18487 Roo.log('onTogglePeriod');
18488 this.time = this.time.add(Date.HOUR, 12);
18495 Roo.apply(Roo.bootstrap.TimeField, {
18525 cls: 'btn btn-info ok',
18537 Roo.apply(Roo.bootstrap.TimeField, {
18541 cls: 'datepicker dropdown-menu',
18545 cls: 'datepicker-time',
18549 cls: 'table-condensed',
18551 Roo.bootstrap.TimeField.content,
18552 Roo.bootstrap.TimeField.footer
18571 * @class Roo.bootstrap.MonthField
18572 * @extends Roo.bootstrap.Input
18573 * Bootstrap MonthField class
18575 * @cfg {String} language default en
18578 * Create a new MonthField
18579 * @param {Object} config The config object
18582 Roo.bootstrap.MonthField = function(config){
18583 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18588 * Fires when this field show.
18589 * @param {Roo.bootstrap.MonthField} this
18590 * @param {Mixed} date The date value
18595 * Fires when this field hide.
18596 * @param {Roo.bootstrap.MonthField} this
18597 * @param {Mixed} date The date value
18602 * Fires when select a date.
18603 * @param {Roo.bootstrap.MonthField} this
18604 * @param {String} oldvalue The old value
18605 * @param {String} newvalue The new value
18611 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18613 onRender: function(ct, position)
18616 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18618 this.language = this.language || 'en';
18619 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18620 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18622 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18623 this.isInline = false;
18624 this.isInput = true;
18625 this.component = this.el.select('.add-on', true).first() || false;
18626 this.component = (this.component && this.component.length === 0) ? false : this.component;
18627 this.hasInput = this.component && this.inputEL().length;
18629 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18631 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18633 this.picker().on('mousedown', this.onMousedown, this);
18634 this.picker().on('click', this.onClick, this);
18636 this.picker().addClass('datepicker-dropdown');
18638 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18639 v.setStyle('width', '189px');
18646 if(this.isInline) {
18652 setValue: function(v, suppressEvent)
18654 var o = this.getValue();
18656 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18660 if(suppressEvent !== true){
18661 this.fireEvent('select', this, o, v);
18666 getValue: function()
18671 onClick: function(e)
18673 e.stopPropagation();
18674 e.preventDefault();
18676 var target = e.getTarget();
18678 if(target.nodeName.toLowerCase() === 'i'){
18679 target = Roo.get(target).dom.parentNode;
18682 var nodeName = target.nodeName;
18683 var className = target.className;
18684 var html = target.innerHTML;
18686 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18690 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18692 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18698 picker : function()
18700 return this.pickerEl;
18703 fillMonths: function()
18706 var months = this.picker().select('>.datepicker-months td', true).first();
18708 months.dom.innerHTML = '';
18714 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18717 months.createChild(month);
18726 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18727 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18730 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18731 e.removeClass('active');
18733 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18734 e.addClass('active');
18741 if(this.isInline) {
18745 this.picker().removeClass(['bottom', 'top']);
18747 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18749 * place to the top of element!
18753 this.picker().addClass('top');
18754 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18759 this.picker().addClass('bottom');
18761 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18764 onFocus : function()
18766 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18770 onBlur : function()
18772 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18774 var d = this.inputEl().getValue();
18783 this.picker().show();
18784 this.picker().select('>.datepicker-months', true).first().show();
18788 this.fireEvent('show', this, this.date);
18793 if(this.isInline) {
18796 this.picker().hide();
18797 this.fireEvent('hide', this, this.date);
18801 onMousedown: function(e)
18803 e.stopPropagation();
18804 e.preventDefault();
18809 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18813 fireKey: function(e)
18815 if (!this.picker().isVisible()){
18816 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18827 e.preventDefault();
18831 dir = e.keyCode == 37 ? -1 : 1;
18833 this.vIndex = this.vIndex + dir;
18835 if(this.vIndex < 0){
18839 if(this.vIndex > 11){
18843 if(isNaN(this.vIndex)){
18847 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18853 dir = e.keyCode == 38 ? -1 : 1;
18855 this.vIndex = this.vIndex + dir * 4;
18857 if(this.vIndex < 0){
18861 if(this.vIndex > 11){
18865 if(isNaN(this.vIndex)){
18869 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18874 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18875 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18879 e.preventDefault();
18882 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18883 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18899 this.picker().remove();
18904 Roo.apply(Roo.bootstrap.MonthField, {
18923 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18924 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18929 Roo.apply(Roo.bootstrap.MonthField, {
18933 cls: 'datepicker dropdown-menu roo-dynamic',
18937 cls: 'datepicker-months',
18941 cls: 'table-condensed',
18943 Roo.bootstrap.DateField.content
18963 * @class Roo.bootstrap.CheckBox
18964 * @extends Roo.bootstrap.Input
18965 * Bootstrap CheckBox class
18967 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18968 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18969 * @cfg {String} boxLabel The text that appears beside the checkbox
18970 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18971 * @cfg {Boolean} checked initnal the element
18972 * @cfg {Boolean} inline inline the element (default false)
18973 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18976 * Create a new CheckBox
18977 * @param {Object} config The config object
18980 Roo.bootstrap.CheckBox = function(config){
18981 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18986 * Fires when the element is checked or unchecked.
18987 * @param {Roo.bootstrap.CheckBox} this This input
18988 * @param {Boolean} checked The new checked value
18995 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18997 inputType: 'checkbox',
19005 getAutoCreate : function()
19007 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19013 cfg.cls = 'form-group ' + this.inputType; //input-group
19016 cfg.cls += ' ' + this.inputType + '-inline';
19022 type : this.inputType,
19023 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19024 cls : 'roo-' + this.inputType, //'form-box',
19025 placeholder : this.placeholder || ''
19029 if (this.weight) { // Validity check?
19030 cfg.cls += " " + this.inputType + "-" + this.weight;
19033 if (this.disabled) {
19034 input.disabled=true;
19038 input.checked = this.checked;
19042 input.name = this.name;
19046 input.cls += ' input-' + this.size;
19051 ['xs','sm','md','lg'].map(function(size){
19052 if (settings[size]) {
19053 cfg.cls += ' col-' + size + '-' + settings[size];
19057 var inputblock = input;
19059 if (this.before || this.after) {
19062 cls : 'input-group',
19067 inputblock.cn.push({
19069 cls : 'input-group-addon',
19074 inputblock.cn.push(input);
19077 inputblock.cn.push({
19079 cls : 'input-group-addon',
19086 if (align ==='left' && this.fieldLabel.length) {
19087 // Roo.log("left and has label");
19093 cls : 'control-label col-md-' + this.labelWidth,
19094 html : this.fieldLabel
19098 cls : "col-md-" + (12 - this.labelWidth),
19105 } else if ( this.fieldLabel.length) {
19106 // Roo.log(" label");
19110 tag: this.boxLabel ? 'span' : 'label',
19112 cls: 'control-label box-input-label',
19113 //cls : 'input-group-addon',
19114 html : this.fieldLabel
19124 // Roo.log(" no label && no align");
19125 cfg.cn = [ inputblock ] ;
19131 var boxLabelCfg = {
19133 //'for': id, // box label is handled by onclick - so no for...
19135 html: this.boxLabel
19139 boxLabelCfg.tooltip = this.tooltip;
19142 cfg.cn.push(boxLabelCfg);
19152 * return the real input element.
19154 inputEl: function ()
19156 return this.el.select('input.roo-' + this.inputType,true).first();
19159 labelEl: function()
19161 return this.el.select('label.control-label',true).first();
19163 /* depricated... */
19167 return this.labelEl();
19170 boxLabelEl: function()
19172 return this.el.select('label.box-label',true).first();
19175 initEvents : function()
19177 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19179 this.inputEl().on('click', this.onClick, this);
19181 if (this.boxLabel) {
19182 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19185 this.startValue = this.getValue();
19188 Roo.bootstrap.CheckBox.register(this);
19192 onClick : function()
19194 this.setChecked(!this.checked);
19197 setChecked : function(state,suppressEvent)
19199 this.startValue = this.getValue();
19201 if(this.inputType == 'radio'){
19203 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19204 e.dom.checked = false;
19207 this.inputEl().dom.checked = true;
19209 this.inputEl().dom.value = this.inputValue;
19211 if(suppressEvent !== true){
19212 this.fireEvent('check', this, true);
19220 this.checked = state;
19222 this.inputEl().dom.checked = state;
19224 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19226 if(suppressEvent !== true){
19227 this.fireEvent('check', this, state);
19233 getValue : function()
19235 if(this.inputType == 'radio'){
19236 return this.getGroupValue();
19239 return this.inputEl().getValue();
19243 getGroupValue : function()
19245 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19249 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19252 setValue : function(v,suppressEvent)
19254 if(this.inputType == 'radio'){
19255 this.setGroupValue(v, suppressEvent);
19259 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19264 setGroupValue : function(v, suppressEvent)
19266 this.startValue = this.getValue();
19268 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19269 e.dom.checked = false;
19271 if(e.dom.value == v){
19272 e.dom.checked = true;
19276 if(suppressEvent !== true){
19277 this.fireEvent('check', this, true);
19285 validate : function()
19289 (this.inputType == 'radio' && this.validateRadio()) ||
19290 (this.inputType == 'checkbox' && this.validateCheckbox())
19296 this.markInvalid();
19300 validateRadio : function()
19304 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19305 if(!e.dom.checked){
19317 validateCheckbox : function()
19320 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19323 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19331 for(var i in group){
19336 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19343 * Mark this field as valid
19345 markValid : function()
19347 if(this.allowBlank){
19353 this.fireEvent('valid', this);
19355 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19358 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19365 if(this.inputType == 'radio'){
19366 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19367 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19368 e.findParent('.form-group', false, true).addClass(_this.validClass);
19375 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19376 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19380 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19386 for(var i in group){
19387 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19388 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19393 * Mark this field as invalid
19394 * @param {String} msg The validation message
19396 markInvalid : function(msg)
19398 if(this.allowBlank){
19404 this.fireEvent('invalid', this, msg);
19406 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19409 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19413 label.markInvalid();
19416 if(this.inputType == 'radio'){
19417 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19418 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19419 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19426 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19427 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19431 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19437 for(var i in group){
19438 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19439 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19446 Roo.apply(Roo.bootstrap.CheckBox, {
19451 * register a CheckBox Group
19452 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19454 register : function(checkbox)
19456 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19457 this.groups[checkbox.groupId] = {};
19460 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19464 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19468 * fetch a CheckBox Group based on the group ID
19469 * @param {string} the group ID
19470 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19472 get: function(groupId) {
19473 if (typeof(this.groups[groupId]) == 'undefined') {
19477 return this.groups[groupId] ;
19489 *<div class="radio">
19491 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19492 Option one is this and that—be sure to include why it's great
19499 *<label class="radio-inline">fieldLabel</label>
19500 *<label class="radio-inline">
19501 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19509 * @class Roo.bootstrap.Radio
19510 * @extends Roo.bootstrap.CheckBox
19511 * Bootstrap Radio class
19514 * Create a new Radio
19515 * @param {Object} config The config object
19518 Roo.bootstrap.Radio = function(config){
19519 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19523 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19525 inputType: 'radio',
19529 getAutoCreate : function()
19531 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19532 align = align || 'left'; // default...
19539 tag : this.inline ? 'span' : 'div',
19544 var inline = this.inline ? ' radio-inline' : '';
19548 // does not need for, as we wrap the input with it..
19550 cls : 'control-label box-label' + inline,
19553 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19557 //cls : 'control-label' + inline,
19558 html : this.fieldLabel,
19559 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19568 type : this.inputType,
19569 //value : (!this.checked) ? this.valueOff : this.inputValue,
19570 value : this.inputValue,
19572 placeholder : this.placeholder || '' // ?? needed????
19575 if (this.weight) { // Validity check?
19576 input.cls += " radio-" + this.weight;
19578 if (this.disabled) {
19579 input.disabled=true;
19583 input.checked = this.checked;
19587 input.name = this.name;
19591 input.cls += ' input-' + this.size;
19594 //?? can span's inline have a width??
19597 ['xs','sm','md','lg'].map(function(size){
19598 if (settings[size]) {
19599 cfg.cls += ' col-' + size + '-' + settings[size];
19603 var inputblock = input;
19605 if (this.before || this.after) {
19608 cls : 'input-group',
19613 inputblock.cn.push({
19615 cls : 'input-group-addon',
19619 inputblock.cn.push(input);
19621 inputblock.cn.push({
19623 cls : 'input-group-addon',
19631 if (this.fieldLabel && this.fieldLabel.length) {
19632 cfg.cn.push(fieldLabel);
19635 // normal bootstrap puts the input inside the label.
19636 // however with our styled version - it has to go after the input.
19638 //lbl.cn.push(inputblock);
19642 cls: 'radio' + inline,
19649 cfg.cn.push( lblwrap);
19654 html: this.boxLabel
19663 initEvents : function()
19665 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19667 this.inputEl().on('click', this.onClick, this);
19668 if (this.boxLabel) {
19669 //Roo.log('find label');
19670 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19675 inputEl: function ()
19677 return this.el.select('input.roo-radio',true).first();
19679 onClick : function()
19682 this.setChecked(true);
19685 setChecked : function(state,suppressEvent)
19688 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19689 v.dom.checked = false;
19692 Roo.log(this.inputEl().dom);
19693 this.checked = state;
19694 this.inputEl().dom.checked = state;
19696 if(suppressEvent !== true){
19697 this.fireEvent('check', this, state);
19700 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19704 getGroupValue : function()
19707 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19708 if(v.dom.checked == true){
19709 value = v.dom.value;
19717 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19718 * @return {Mixed} value The field value
19720 getValue : function(){
19721 return this.getGroupValue();
19727 //<script type="text/javascript">
19730 * Based Ext JS Library 1.1.1
19731 * Copyright(c) 2006-2007, Ext JS, LLC.
19737 * @class Roo.HtmlEditorCore
19738 * @extends Roo.Component
19739 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19741 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19744 Roo.HtmlEditorCore = function(config){
19747 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19752 * @event initialize
19753 * Fires when the editor is fully initialized (including the iframe)
19754 * @param {Roo.HtmlEditorCore} this
19759 * Fires when the editor is first receives the focus. Any insertion must wait
19760 * until after this event.
19761 * @param {Roo.HtmlEditorCore} this
19765 * @event beforesync
19766 * Fires before the textarea is updated with content from the editor iframe. Return false
19767 * to cancel the sync.
19768 * @param {Roo.HtmlEditorCore} this
19769 * @param {String} html
19773 * @event beforepush
19774 * Fires before the iframe editor is updated with content from the textarea. Return false
19775 * to cancel the push.
19776 * @param {Roo.HtmlEditorCore} this
19777 * @param {String} html
19782 * Fires when the textarea is updated with content from the editor iframe.
19783 * @param {Roo.HtmlEditorCore} this
19784 * @param {String} html
19789 * Fires when the iframe editor is updated with content from the textarea.
19790 * @param {Roo.HtmlEditorCore} this
19791 * @param {String} html
19796 * @event editorevent
19797 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19798 * @param {Roo.HtmlEditorCore} this
19804 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19806 // defaults : white / black...
19807 this.applyBlacklists();
19814 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19818 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19824 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19829 * @cfg {Number} height (in pixels)
19833 * @cfg {Number} width (in pixels)
19838 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19841 stylesheets: false,
19846 // private properties
19847 validationEvent : false,
19849 initialized : false,
19851 sourceEditMode : false,
19852 onFocus : Roo.emptyFn,
19854 hideMode:'offsets',
19858 // blacklist + whitelisted elements..
19865 * Protected method that will not generally be called directly. It
19866 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19867 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19869 getDocMarkup : function(){
19873 // inherit styels from page...??
19874 if (this.stylesheets === false) {
19876 Roo.get(document.head).select('style').each(function(node) {
19877 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19880 Roo.get(document.head).select('link').each(function(node) {
19881 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19884 } else if (!this.stylesheets.length) {
19886 st = '<style type="text/css">' +
19887 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19893 st += '<style type="text/css">' +
19894 'IMG { cursor: pointer } ' +
19898 return '<html><head>' + st +
19899 //<style type="text/css">' +
19900 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19902 ' </head><body class="roo-htmleditor-body"></body></html>';
19906 onRender : function(ct, position)
19909 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19910 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19913 this.el.dom.style.border = '0 none';
19914 this.el.dom.setAttribute('tabIndex', -1);
19915 this.el.addClass('x-hidden hide');
19919 if(Roo.isIE){ // fix IE 1px bogus margin
19920 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19924 this.frameId = Roo.id();
19928 var iframe = this.owner.wrap.createChild({
19930 cls: 'form-control', // bootstrap..
19932 name: this.frameId,
19933 frameBorder : 'no',
19934 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19939 this.iframe = iframe.dom;
19941 this.assignDocWin();
19943 this.doc.designMode = 'on';
19946 this.doc.write(this.getDocMarkup());
19950 var task = { // must defer to wait for browser to be ready
19952 //console.log("run task?" + this.doc.readyState);
19953 this.assignDocWin();
19954 if(this.doc.body || this.doc.readyState == 'complete'){
19956 this.doc.designMode="on";
19960 Roo.TaskMgr.stop(task);
19961 this.initEditor.defer(10, this);
19968 Roo.TaskMgr.start(task);
19973 onResize : function(w, h)
19975 Roo.log('resize: ' +w + ',' + h );
19976 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19980 if(typeof w == 'number'){
19982 this.iframe.style.width = w + 'px';
19984 if(typeof h == 'number'){
19986 this.iframe.style.height = h + 'px';
19988 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19995 * Toggles the editor between standard and source edit mode.
19996 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19998 toggleSourceEdit : function(sourceEditMode){
20000 this.sourceEditMode = sourceEditMode === true;
20002 if(this.sourceEditMode){
20004 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20007 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20008 //this.iframe.className = '';
20011 //this.setSize(this.owner.wrap.getSize());
20012 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20019 * Protected method that will not generally be called directly. If you need/want
20020 * custom HTML cleanup, this is the method you should override.
20021 * @param {String} html The HTML to be cleaned
20022 * return {String} The cleaned HTML
20024 cleanHtml : function(html){
20025 html = String(html);
20026 if(html.length > 5){
20027 if(Roo.isSafari){ // strip safari nonsense
20028 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20031 if(html == ' '){
20038 * HTML Editor -> Textarea
20039 * Protected method that will not generally be called directly. Syncs the contents
20040 * of the editor iframe with the textarea.
20042 syncValue : function(){
20043 if(this.initialized){
20044 var bd = (this.doc.body || this.doc.documentElement);
20045 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20046 var html = bd.innerHTML;
20048 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20049 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20051 html = '<div style="'+m[0]+'">' + html + '</div>';
20054 html = this.cleanHtml(html);
20055 // fix up the special chars.. normaly like back quotes in word...
20056 // however we do not want to do this with chinese..
20057 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20058 var cc = b.charCodeAt();
20060 (cc >= 0x4E00 && cc < 0xA000 ) ||
20061 (cc >= 0x3400 && cc < 0x4E00 ) ||
20062 (cc >= 0xf900 && cc < 0xfb00 )
20068 if(this.owner.fireEvent('beforesync', this, html) !== false){
20069 this.el.dom.value = html;
20070 this.owner.fireEvent('sync', this, html);
20076 * Protected method that will not generally be called directly. Pushes the value of the textarea
20077 * into the iframe editor.
20079 pushValue : function(){
20080 if(this.initialized){
20081 var v = this.el.dom.value.trim();
20083 // if(v.length < 1){
20087 if(this.owner.fireEvent('beforepush', this, v) !== false){
20088 var d = (this.doc.body || this.doc.documentElement);
20090 this.cleanUpPaste();
20091 this.el.dom.value = d.innerHTML;
20092 this.owner.fireEvent('push', this, v);
20098 deferFocus : function(){
20099 this.focus.defer(10, this);
20103 focus : function(){
20104 if(this.win && !this.sourceEditMode){
20111 assignDocWin: function()
20113 var iframe = this.iframe;
20116 this.doc = iframe.contentWindow.document;
20117 this.win = iframe.contentWindow;
20119 // if (!Roo.get(this.frameId)) {
20122 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20123 // this.win = Roo.get(this.frameId).dom.contentWindow;
20125 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20129 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20130 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20135 initEditor : function(){
20136 //console.log("INIT EDITOR");
20137 this.assignDocWin();
20141 this.doc.designMode="on";
20143 this.doc.write(this.getDocMarkup());
20146 var dbody = (this.doc.body || this.doc.documentElement);
20147 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20148 // this copies styles from the containing element into thsi one..
20149 // not sure why we need all of this..
20150 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20152 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20153 //ss['background-attachment'] = 'fixed'; // w3c
20154 dbody.bgProperties = 'fixed'; // ie
20155 //Roo.DomHelper.applyStyles(dbody, ss);
20156 Roo.EventManager.on(this.doc, {
20157 //'mousedown': this.onEditorEvent,
20158 'mouseup': this.onEditorEvent,
20159 'dblclick': this.onEditorEvent,
20160 'click': this.onEditorEvent,
20161 'keyup': this.onEditorEvent,
20166 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20168 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20169 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20171 this.initialized = true;
20173 this.owner.fireEvent('initialize', this);
20178 onDestroy : function(){
20184 //for (var i =0; i < this.toolbars.length;i++) {
20185 // // fixme - ask toolbars for heights?
20186 // this.toolbars[i].onDestroy();
20189 //this.wrap.dom.innerHTML = '';
20190 //this.wrap.remove();
20195 onFirstFocus : function(){
20197 this.assignDocWin();
20200 this.activated = true;
20203 if(Roo.isGecko){ // prevent silly gecko errors
20205 var s = this.win.getSelection();
20206 if(!s.focusNode || s.focusNode.nodeType != 3){
20207 var r = s.getRangeAt(0);
20208 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20213 this.execCmd('useCSS', true);
20214 this.execCmd('styleWithCSS', false);
20217 this.owner.fireEvent('activate', this);
20221 adjustFont: function(btn){
20222 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20223 //if(Roo.isSafari){ // safari
20226 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20227 if(Roo.isSafari){ // safari
20228 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20229 v = (v < 10) ? 10 : v;
20230 v = (v > 48) ? 48 : v;
20231 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20236 v = Math.max(1, v+adjust);
20238 this.execCmd('FontSize', v );
20241 onEditorEvent : function(e)
20243 this.owner.fireEvent('editorevent', this, e);
20244 // this.updateToolbar();
20245 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20248 insertTag : function(tg)
20250 // could be a bit smarter... -> wrap the current selected tRoo..
20251 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20253 range = this.createRange(this.getSelection());
20254 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20255 wrappingNode.appendChild(range.extractContents());
20256 range.insertNode(wrappingNode);
20263 this.execCmd("formatblock", tg);
20267 insertText : function(txt)
20271 var range = this.createRange();
20272 range.deleteContents();
20273 //alert(Sender.getAttribute('label'));
20275 range.insertNode(this.doc.createTextNode(txt));
20281 * Executes a Midas editor command on the editor document and performs necessary focus and
20282 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20283 * @param {String} cmd The Midas command
20284 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20286 relayCmd : function(cmd, value){
20288 this.execCmd(cmd, value);
20289 this.owner.fireEvent('editorevent', this);
20290 //this.updateToolbar();
20291 this.owner.deferFocus();
20295 * Executes a Midas editor command directly on the editor document.
20296 * For visual commands, you should use {@link #relayCmd} instead.
20297 * <b>This should only be called after the editor is initialized.</b>
20298 * @param {String} cmd The Midas command
20299 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20301 execCmd : function(cmd, value){
20302 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20309 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20311 * @param {String} text | dom node..
20313 insertAtCursor : function(text)
20318 if(!this.activated){
20324 var r = this.doc.selection.createRange();
20335 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20339 // from jquery ui (MIT licenced)
20341 var win = this.win;
20343 if (win.getSelection && win.getSelection().getRangeAt) {
20344 range = win.getSelection().getRangeAt(0);
20345 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20346 range.insertNode(node);
20347 } else if (win.document.selection && win.document.selection.createRange) {
20348 // no firefox support
20349 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20350 win.document.selection.createRange().pasteHTML(txt);
20352 // no firefox support
20353 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20354 this.execCmd('InsertHTML', txt);
20363 mozKeyPress : function(e){
20365 var c = e.getCharCode(), cmd;
20368 c = String.fromCharCode(c).toLowerCase();
20382 this.cleanUpPaste.defer(100, this);
20390 e.preventDefault();
20398 fixKeys : function(){ // load time branching for fastest keydown performance
20400 return function(e){
20401 var k = e.getKey(), r;
20404 r = this.doc.selection.createRange();
20407 r.pasteHTML('    ');
20414 r = this.doc.selection.createRange();
20416 var target = r.parentElement();
20417 if(!target || target.tagName.toLowerCase() != 'li'){
20419 r.pasteHTML('<br />');
20425 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20426 this.cleanUpPaste.defer(100, this);
20432 }else if(Roo.isOpera){
20433 return function(e){
20434 var k = e.getKey();
20438 this.execCmd('InsertHTML','    ');
20441 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20442 this.cleanUpPaste.defer(100, this);
20447 }else if(Roo.isSafari){
20448 return function(e){
20449 var k = e.getKey();
20453 this.execCmd('InsertText','\t');
20457 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20458 this.cleanUpPaste.defer(100, this);
20466 getAllAncestors: function()
20468 var p = this.getSelectedNode();
20471 a.push(p); // push blank onto stack..
20472 p = this.getParentElement();
20476 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20480 a.push(this.doc.body);
20484 lastSelNode : false,
20487 getSelection : function()
20489 this.assignDocWin();
20490 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20493 getSelectedNode: function()
20495 // this may only work on Gecko!!!
20497 // should we cache this!!!!
20502 var range = this.createRange(this.getSelection()).cloneRange();
20505 var parent = range.parentElement();
20507 var testRange = range.duplicate();
20508 testRange.moveToElementText(parent);
20509 if (testRange.inRange(range)) {
20512 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20515 parent = parent.parentElement;
20520 // is ancestor a text element.
20521 var ac = range.commonAncestorContainer;
20522 if (ac.nodeType == 3) {
20523 ac = ac.parentNode;
20526 var ar = ac.childNodes;
20529 var other_nodes = [];
20530 var has_other_nodes = false;
20531 for (var i=0;i<ar.length;i++) {
20532 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20535 // fullly contained node.
20537 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20542 // probably selected..
20543 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20544 other_nodes.push(ar[i]);
20548 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20553 has_other_nodes = true;
20555 if (!nodes.length && other_nodes.length) {
20556 nodes= other_nodes;
20558 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20564 createRange: function(sel)
20566 // this has strange effects when using with
20567 // top toolbar - not sure if it's a great idea.
20568 //this.editor.contentWindow.focus();
20569 if (typeof sel != "undefined") {
20571 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20573 return this.doc.createRange();
20576 return this.doc.createRange();
20579 getParentElement: function()
20582 this.assignDocWin();
20583 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20585 var range = this.createRange(sel);
20588 var p = range.commonAncestorContainer;
20589 while (p.nodeType == 3) { // text node
20600 * Range intersection.. the hard stuff...
20604 * [ -- selected range --- ]
20608 * if end is before start or hits it. fail.
20609 * if start is after end or hits it fail.
20611 * if either hits (but other is outside. - then it's not
20617 // @see http://www.thismuchiknow.co.uk/?p=64.
20618 rangeIntersectsNode : function(range, node)
20620 var nodeRange = node.ownerDocument.createRange();
20622 nodeRange.selectNode(node);
20624 nodeRange.selectNodeContents(node);
20627 var rangeStartRange = range.cloneRange();
20628 rangeStartRange.collapse(true);
20630 var rangeEndRange = range.cloneRange();
20631 rangeEndRange.collapse(false);
20633 var nodeStartRange = nodeRange.cloneRange();
20634 nodeStartRange.collapse(true);
20636 var nodeEndRange = nodeRange.cloneRange();
20637 nodeEndRange.collapse(false);
20639 return rangeStartRange.compareBoundaryPoints(
20640 Range.START_TO_START, nodeEndRange) == -1 &&
20641 rangeEndRange.compareBoundaryPoints(
20642 Range.START_TO_START, nodeStartRange) == 1;
20646 rangeCompareNode : function(range, node)
20648 var nodeRange = node.ownerDocument.createRange();
20650 nodeRange.selectNode(node);
20652 nodeRange.selectNodeContents(node);
20656 range.collapse(true);
20658 nodeRange.collapse(true);
20660 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20661 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20663 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20665 var nodeIsBefore = ss == 1;
20666 var nodeIsAfter = ee == -1;
20668 if (nodeIsBefore && nodeIsAfter) {
20671 if (!nodeIsBefore && nodeIsAfter) {
20672 return 1; //right trailed.
20675 if (nodeIsBefore && !nodeIsAfter) {
20676 return 2; // left trailed.
20682 // private? - in a new class?
20683 cleanUpPaste : function()
20685 // cleans up the whole document..
20686 Roo.log('cleanuppaste');
20688 this.cleanUpChildren(this.doc.body);
20689 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20690 if (clean != this.doc.body.innerHTML) {
20691 this.doc.body.innerHTML = clean;
20696 cleanWordChars : function(input) {// change the chars to hex code
20697 var he = Roo.HtmlEditorCore;
20699 var output = input;
20700 Roo.each(he.swapCodes, function(sw) {
20701 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20703 output = output.replace(swapper, sw[1]);
20710 cleanUpChildren : function (n)
20712 if (!n.childNodes.length) {
20715 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20716 this.cleanUpChild(n.childNodes[i]);
20723 cleanUpChild : function (node)
20726 //console.log(node);
20727 if (node.nodeName == "#text") {
20728 // clean up silly Windows -- stuff?
20731 if (node.nodeName == "#comment") {
20732 node.parentNode.removeChild(node);
20733 // clean up silly Windows -- stuff?
20736 var lcname = node.tagName.toLowerCase();
20737 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20738 // whitelist of tags..
20740 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20742 node.parentNode.removeChild(node);
20747 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20749 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20750 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20752 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20753 // remove_keep_children = true;
20756 if (remove_keep_children) {
20757 this.cleanUpChildren(node);
20758 // inserts everything just before this node...
20759 while (node.childNodes.length) {
20760 var cn = node.childNodes[0];
20761 node.removeChild(cn);
20762 node.parentNode.insertBefore(cn, node);
20764 node.parentNode.removeChild(node);
20768 if (!node.attributes || !node.attributes.length) {
20769 this.cleanUpChildren(node);
20773 function cleanAttr(n,v)
20776 if (v.match(/^\./) || v.match(/^\//)) {
20779 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20782 if (v.match(/^#/)) {
20785 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20786 node.removeAttribute(n);
20790 var cwhite = this.cwhite;
20791 var cblack = this.cblack;
20793 function cleanStyle(n,v)
20795 if (v.match(/expression/)) { //XSS?? should we even bother..
20796 node.removeAttribute(n);
20800 var parts = v.split(/;/);
20803 Roo.each(parts, function(p) {
20804 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20808 var l = p.split(':').shift().replace(/\s+/g,'');
20809 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20811 if ( cwhite.length && cblack.indexOf(l) > -1) {
20812 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20813 //node.removeAttribute(n);
20817 // only allow 'c whitelisted system attributes'
20818 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20819 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20820 //node.removeAttribute(n);
20830 if (clean.length) {
20831 node.setAttribute(n, clean.join(';'));
20833 node.removeAttribute(n);
20839 for (var i = node.attributes.length-1; i > -1 ; i--) {
20840 var a = node.attributes[i];
20843 if (a.name.toLowerCase().substr(0,2)=='on') {
20844 node.removeAttribute(a.name);
20847 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20848 node.removeAttribute(a.name);
20851 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20852 cleanAttr(a.name,a.value); // fixme..
20855 if (a.name == 'style') {
20856 cleanStyle(a.name,a.value);
20859 /// clean up MS crap..
20860 // tecnically this should be a list of valid class'es..
20863 if (a.name == 'class') {
20864 if (a.value.match(/^Mso/)) {
20865 node.className = '';
20868 if (a.value.match(/body/)) {
20869 node.className = '';
20880 this.cleanUpChildren(node);
20886 * Clean up MS wordisms...
20888 cleanWord : function(node)
20893 this.cleanWord(this.doc.body);
20896 if (node.nodeName == "#text") {
20897 // clean up silly Windows -- stuff?
20900 if (node.nodeName == "#comment") {
20901 node.parentNode.removeChild(node);
20902 // clean up silly Windows -- stuff?
20906 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20907 node.parentNode.removeChild(node);
20911 // remove - but keep children..
20912 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20913 while (node.childNodes.length) {
20914 var cn = node.childNodes[0];
20915 node.removeChild(cn);
20916 node.parentNode.insertBefore(cn, node);
20918 node.parentNode.removeChild(node);
20919 this.iterateChildren(node, this.cleanWord);
20923 if (node.className.length) {
20925 var cn = node.className.split(/\W+/);
20927 Roo.each(cn, function(cls) {
20928 if (cls.match(/Mso[a-zA-Z]+/)) {
20933 node.className = cna.length ? cna.join(' ') : '';
20935 node.removeAttribute("class");
20939 if (node.hasAttribute("lang")) {
20940 node.removeAttribute("lang");
20943 if (node.hasAttribute("style")) {
20945 var styles = node.getAttribute("style").split(";");
20947 Roo.each(styles, function(s) {
20948 if (!s.match(/:/)) {
20951 var kv = s.split(":");
20952 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20955 // what ever is left... we allow.
20958 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20959 if (!nstyle.length) {
20960 node.removeAttribute('style');
20963 this.iterateChildren(node, this.cleanWord);
20969 * iterateChildren of a Node, calling fn each time, using this as the scole..
20970 * @param {DomNode} node node to iterate children of.
20971 * @param {Function} fn method of this class to call on each item.
20973 iterateChildren : function(node, fn)
20975 if (!node.childNodes.length) {
20978 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20979 fn.call(this, node.childNodes[i])
20985 * cleanTableWidths.
20987 * Quite often pasting from word etc.. results in tables with column and widths.
20988 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20991 cleanTableWidths : function(node)
20996 this.cleanTableWidths(this.doc.body);
21001 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21004 Roo.log(node.tagName);
21005 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21006 this.iterateChildren(node, this.cleanTableWidths);
21009 if (node.hasAttribute('width')) {
21010 node.removeAttribute('width');
21014 if (node.hasAttribute("style")) {
21017 var styles = node.getAttribute("style").split(";");
21019 Roo.each(styles, function(s) {
21020 if (!s.match(/:/)) {
21023 var kv = s.split(":");
21024 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21027 // what ever is left... we allow.
21030 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21031 if (!nstyle.length) {
21032 node.removeAttribute('style');
21036 this.iterateChildren(node, this.cleanTableWidths);
21044 domToHTML : function(currentElement, depth, nopadtext) {
21046 depth = depth || 0;
21047 nopadtext = nopadtext || false;
21049 if (!currentElement) {
21050 return this.domToHTML(this.doc.body);
21053 //Roo.log(currentElement);
21055 var allText = false;
21056 var nodeName = currentElement.nodeName;
21057 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21059 if (nodeName == '#text') {
21061 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21066 if (nodeName != 'BODY') {
21069 // Prints the node tagName, such as <A>, <IMG>, etc
21072 for(i = 0; i < currentElement.attributes.length;i++) {
21074 var aname = currentElement.attributes.item(i).name;
21075 if (!currentElement.attributes.item(i).value.length) {
21078 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21081 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21090 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21093 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21098 // Traverse the tree
21100 var currentElementChild = currentElement.childNodes.item(i);
21101 var allText = true;
21102 var innerHTML = '';
21104 while (currentElementChild) {
21105 // Formatting code (indent the tree so it looks nice on the screen)
21106 var nopad = nopadtext;
21107 if (lastnode == 'SPAN') {
21111 if (currentElementChild.nodeName == '#text') {
21112 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21113 toadd = nopadtext ? toadd : toadd.trim();
21114 if (!nopad && toadd.length > 80) {
21115 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21117 innerHTML += toadd;
21120 currentElementChild = currentElement.childNodes.item(i);
21126 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21128 // Recursively traverse the tree structure of the child node
21129 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21130 lastnode = currentElementChild.nodeName;
21132 currentElementChild=currentElement.childNodes.item(i);
21138 // The remaining code is mostly for formatting the tree
21139 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21144 ret+= "</"+tagName+">";
21150 applyBlacklists : function()
21152 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21153 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21157 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21158 if (b.indexOf(tag) > -1) {
21161 this.white.push(tag);
21165 Roo.each(w, function(tag) {
21166 if (b.indexOf(tag) > -1) {
21169 if (this.white.indexOf(tag) > -1) {
21172 this.white.push(tag);
21177 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21178 if (w.indexOf(tag) > -1) {
21181 this.black.push(tag);
21185 Roo.each(b, function(tag) {
21186 if (w.indexOf(tag) > -1) {
21189 if (this.black.indexOf(tag) > -1) {
21192 this.black.push(tag);
21197 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21198 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21202 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21203 if (b.indexOf(tag) > -1) {
21206 this.cwhite.push(tag);
21210 Roo.each(w, function(tag) {
21211 if (b.indexOf(tag) > -1) {
21214 if (this.cwhite.indexOf(tag) > -1) {
21217 this.cwhite.push(tag);
21222 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21223 if (w.indexOf(tag) > -1) {
21226 this.cblack.push(tag);
21230 Roo.each(b, function(tag) {
21231 if (w.indexOf(tag) > -1) {
21234 if (this.cblack.indexOf(tag) > -1) {
21237 this.cblack.push(tag);
21242 setStylesheets : function(stylesheets)
21244 if(typeof(stylesheets) == 'string'){
21245 Roo.get(this.iframe.contentDocument.head).createChild({
21247 rel : 'stylesheet',
21256 Roo.each(stylesheets, function(s) {
21261 Roo.get(_this.iframe.contentDocument.head).createChild({
21263 rel : 'stylesheet',
21272 removeStylesheets : function()
21276 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21281 // hide stuff that is not compatible
21295 * @event specialkey
21299 * @cfg {String} fieldClass @hide
21302 * @cfg {String} focusClass @hide
21305 * @cfg {String} autoCreate @hide
21308 * @cfg {String} inputType @hide
21311 * @cfg {String} invalidClass @hide
21314 * @cfg {String} invalidText @hide
21317 * @cfg {String} msgFx @hide
21320 * @cfg {String} validateOnBlur @hide
21324 Roo.HtmlEditorCore.white = [
21325 'area', 'br', 'img', 'input', 'hr', 'wbr',
21327 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21328 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21329 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21330 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21331 'table', 'ul', 'xmp',
21333 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21336 'dir', 'menu', 'ol', 'ul', 'dl',
21342 Roo.HtmlEditorCore.black = [
21343 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21345 'base', 'basefont', 'bgsound', 'blink', 'body',
21346 'frame', 'frameset', 'head', 'html', 'ilayer',
21347 'iframe', 'layer', 'link', 'meta', 'object',
21348 'script', 'style' ,'title', 'xml' // clean later..
21350 Roo.HtmlEditorCore.clean = [
21351 'script', 'style', 'title', 'xml'
21353 Roo.HtmlEditorCore.remove = [
21358 Roo.HtmlEditorCore.ablack = [
21362 Roo.HtmlEditorCore.aclean = [
21363 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21367 Roo.HtmlEditorCore.pwhite= [
21368 'http', 'https', 'mailto'
21371 // white listed style attributes.
21372 Roo.HtmlEditorCore.cwhite= [
21373 // 'text-align', /// default is to allow most things..
21379 // black listed style attributes.
21380 Roo.HtmlEditorCore.cblack= [
21381 // 'font-size' -- this can be set by the project
21385 Roo.HtmlEditorCore.swapCodes =[
21404 * @class Roo.bootstrap.HtmlEditor
21405 * @extends Roo.bootstrap.TextArea
21406 * Bootstrap HtmlEditor class
21409 * Create a new HtmlEditor
21410 * @param {Object} config The config object
21413 Roo.bootstrap.HtmlEditor = function(config){
21414 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21415 if (!this.toolbars) {
21416 this.toolbars = [];
21418 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21421 * @event initialize
21422 * Fires when the editor is fully initialized (including the iframe)
21423 * @param {HtmlEditor} this
21428 * Fires when the editor is first receives the focus. Any insertion must wait
21429 * until after this event.
21430 * @param {HtmlEditor} this
21434 * @event beforesync
21435 * Fires before the textarea is updated with content from the editor iframe. Return false
21436 * to cancel the sync.
21437 * @param {HtmlEditor} this
21438 * @param {String} html
21442 * @event beforepush
21443 * Fires before the iframe editor is updated with content from the textarea. Return false
21444 * to cancel the push.
21445 * @param {HtmlEditor} this
21446 * @param {String} html
21451 * Fires when the textarea is updated with content from the editor iframe.
21452 * @param {HtmlEditor} this
21453 * @param {String} html
21458 * Fires when the iframe editor is updated with content from the textarea.
21459 * @param {HtmlEditor} this
21460 * @param {String} html
21464 * @event editmodechange
21465 * Fires when the editor switches edit modes
21466 * @param {HtmlEditor} this
21467 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21469 editmodechange: true,
21471 * @event editorevent
21472 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21473 * @param {HtmlEditor} this
21477 * @event firstfocus
21478 * Fires when on first focus - needed by toolbars..
21479 * @param {HtmlEditor} this
21484 * Auto save the htmlEditor value as a file into Events
21485 * @param {HtmlEditor} this
21489 * @event savedpreview
21490 * preview the saved version of htmlEditor
21491 * @param {HtmlEditor} this
21498 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21502 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21507 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21512 * @cfg {Number} height (in pixels)
21516 * @cfg {Number} width (in pixels)
21521 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21524 stylesheets: false,
21529 // private properties
21530 validationEvent : false,
21532 initialized : false,
21535 onFocus : Roo.emptyFn,
21537 hideMode:'offsets',
21540 tbContainer : false,
21542 toolbarContainer :function() {
21543 return this.wrap.select('.x-html-editor-tb',true).first();
21547 * Protected method that will not generally be called directly. It
21548 * is called when the editor creates its toolbar. Override this method if you need to
21549 * add custom toolbar buttons.
21550 * @param {HtmlEditor} editor
21552 createToolbar : function(){
21554 Roo.log("create toolbars");
21556 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21557 this.toolbars[0].render(this.toolbarContainer());
21561 // if (!editor.toolbars || !editor.toolbars.length) {
21562 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21565 // for (var i =0 ; i < editor.toolbars.length;i++) {
21566 // editor.toolbars[i] = Roo.factory(
21567 // typeof(editor.toolbars[i]) == 'string' ?
21568 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21569 // Roo.bootstrap.HtmlEditor);
21570 // editor.toolbars[i].init(editor);
21576 onRender : function(ct, position)
21578 // Roo.log("Call onRender: " + this.xtype);
21580 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21582 this.wrap = this.inputEl().wrap({
21583 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21586 this.editorcore.onRender(ct, position);
21588 if (this.resizable) {
21589 this.resizeEl = new Roo.Resizable(this.wrap, {
21593 minHeight : this.height,
21594 height: this.height,
21595 handles : this.resizable,
21598 resize : function(r, w, h) {
21599 _t.onResize(w,h); // -something
21605 this.createToolbar(this);
21608 if(!this.width && this.resizable){
21609 this.setSize(this.wrap.getSize());
21611 if (this.resizeEl) {
21612 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21613 // should trigger onReize..
21619 onResize : function(w, h)
21621 Roo.log('resize: ' +w + ',' + h );
21622 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21626 if(this.inputEl() ){
21627 if(typeof w == 'number'){
21628 var aw = w - this.wrap.getFrameWidth('lr');
21629 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21632 if(typeof h == 'number'){
21633 var tbh = -11; // fixme it needs to tool bar size!
21634 for (var i =0; i < this.toolbars.length;i++) {
21635 // fixme - ask toolbars for heights?
21636 tbh += this.toolbars[i].el.getHeight();
21637 //if (this.toolbars[i].footer) {
21638 // tbh += this.toolbars[i].footer.el.getHeight();
21646 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21647 ah -= 5; // knock a few pixes off for look..
21648 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21652 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21653 this.editorcore.onResize(ew,eh);
21658 * Toggles the editor between standard and source edit mode.
21659 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21661 toggleSourceEdit : function(sourceEditMode)
21663 this.editorcore.toggleSourceEdit(sourceEditMode);
21665 if(this.editorcore.sourceEditMode){
21666 Roo.log('editor - showing textarea');
21669 // Roo.log(this.syncValue());
21671 this.inputEl().removeClass(['hide', 'x-hidden']);
21672 this.inputEl().dom.removeAttribute('tabIndex');
21673 this.inputEl().focus();
21675 Roo.log('editor - hiding textarea');
21677 // Roo.log(this.pushValue());
21680 this.inputEl().addClass(['hide', 'x-hidden']);
21681 this.inputEl().dom.setAttribute('tabIndex', -1);
21682 //this.deferFocus();
21685 if(this.resizable){
21686 this.setSize(this.wrap.getSize());
21689 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21692 // private (for BoxComponent)
21693 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21695 // private (for BoxComponent)
21696 getResizeEl : function(){
21700 // private (for BoxComponent)
21701 getPositionEl : function(){
21706 initEvents : function(){
21707 this.originalValue = this.getValue();
21711 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21714 // markInvalid : Roo.emptyFn,
21716 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21719 // clearInvalid : Roo.emptyFn,
21721 setValue : function(v){
21722 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21723 this.editorcore.pushValue();
21728 deferFocus : function(){
21729 this.focus.defer(10, this);
21733 focus : function(){
21734 this.editorcore.focus();
21740 onDestroy : function(){
21746 for (var i =0; i < this.toolbars.length;i++) {
21747 // fixme - ask toolbars for heights?
21748 this.toolbars[i].onDestroy();
21751 this.wrap.dom.innerHTML = '';
21752 this.wrap.remove();
21757 onFirstFocus : function(){
21758 //Roo.log("onFirstFocus");
21759 this.editorcore.onFirstFocus();
21760 for (var i =0; i < this.toolbars.length;i++) {
21761 this.toolbars[i].onFirstFocus();
21767 syncValue : function()
21769 this.editorcore.syncValue();
21772 pushValue : function()
21774 this.editorcore.pushValue();
21778 // hide stuff that is not compatible
21792 * @event specialkey
21796 * @cfg {String} fieldClass @hide
21799 * @cfg {String} focusClass @hide
21802 * @cfg {String} autoCreate @hide
21805 * @cfg {String} inputType @hide
21808 * @cfg {String} invalidClass @hide
21811 * @cfg {String} invalidText @hide
21814 * @cfg {String} msgFx @hide
21817 * @cfg {String} validateOnBlur @hide
21826 Roo.namespace('Roo.bootstrap.htmleditor');
21828 * @class Roo.bootstrap.HtmlEditorToolbar1
21833 new Roo.bootstrap.HtmlEditor({
21836 new Roo.bootstrap.HtmlEditorToolbar1({
21837 disable : { fonts: 1 , format: 1, ..., ... , ...],
21843 * @cfg {Object} disable List of elements to disable..
21844 * @cfg {Array} btns List of additional buttons.
21848 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21851 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21854 Roo.apply(this, config);
21856 // default disabled, based on 'good practice'..
21857 this.disable = this.disable || {};
21858 Roo.applyIf(this.disable, {
21861 specialElements : true
21863 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21865 this.editor = config.editor;
21866 this.editorcore = config.editor.editorcore;
21868 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21870 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21871 // dont call parent... till later.
21873 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21878 editorcore : false,
21883 "h1","h2","h3","h4","h5","h6",
21885 "abbr", "acronym", "address", "cite", "samp", "var",
21889 onRender : function(ct, position)
21891 // Roo.log("Call onRender: " + this.xtype);
21893 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21895 this.el.dom.style.marginBottom = '0';
21897 var editorcore = this.editorcore;
21898 var editor= this.editor;
21901 var btn = function(id,cmd , toggle, handler){
21903 var event = toggle ? 'toggle' : 'click';
21908 xns: Roo.bootstrap,
21911 enableToggle:toggle !== false,
21913 pressed : toggle ? false : null,
21916 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21917 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21926 xns: Roo.bootstrap,
21927 glyphicon : 'font',
21931 xns: Roo.bootstrap,
21935 Roo.each(this.formats, function(f) {
21936 style.menu.items.push({
21938 xns: Roo.bootstrap,
21939 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21944 editorcore.insertTag(this.tagname);
21951 children.push(style);
21954 btn('bold',false,true);
21955 btn('italic',false,true);
21956 btn('align-left', 'justifyleft',true);
21957 btn('align-center', 'justifycenter',true);
21958 btn('align-right' , 'justifyright',true);
21959 btn('link', false, false, function(btn) {
21960 //Roo.log("create link?");
21961 var url = prompt(this.createLinkText, this.defaultLinkValue);
21962 if(url && url != 'http:/'+'/'){
21963 this.editorcore.relayCmd('createlink', url);
21966 btn('list','insertunorderedlist',true);
21967 btn('pencil', false,true, function(btn){
21970 this.toggleSourceEdit(btn.pressed);
21976 xns: Roo.bootstrap,
21981 xns: Roo.bootstrap,
21986 cog.menu.items.push({
21988 xns: Roo.bootstrap,
21989 html : Clean styles,
21994 editorcore.insertTag(this.tagname);
22003 this.xtype = 'NavSimplebar';
22005 for(var i=0;i< children.length;i++) {
22007 this.buttons.add(this.addxtypeChild(children[i]));
22011 editor.on('editorevent', this.updateToolbar, this);
22013 onBtnClick : function(id)
22015 this.editorcore.relayCmd(id);
22016 this.editorcore.focus();
22020 * Protected method that will not generally be called directly. It triggers
22021 * a toolbar update by reading the markup state of the current selection in the editor.
22023 updateToolbar: function(){
22025 if(!this.editorcore.activated){
22026 this.editor.onFirstFocus(); // is this neeed?
22030 var btns = this.buttons;
22031 var doc = this.editorcore.doc;
22032 btns.get('bold').setActive(doc.queryCommandState('bold'));
22033 btns.get('italic').setActive(doc.queryCommandState('italic'));
22034 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22036 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22037 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22038 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22040 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22041 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22044 var ans = this.editorcore.getAllAncestors();
22045 if (this.formatCombo) {
22048 var store = this.formatCombo.store;
22049 this.formatCombo.setValue("");
22050 for (var i =0; i < ans.length;i++) {
22051 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22053 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22061 // hides menus... - so this cant be on a menu...
22062 Roo.bootstrap.MenuMgr.hideAll();
22064 Roo.bootstrap.MenuMgr.hideAll();
22065 //this.editorsyncValue();
22067 onFirstFocus: function() {
22068 this.buttons.each(function(item){
22072 toggleSourceEdit : function(sourceEditMode){
22075 if(sourceEditMode){
22076 Roo.log("disabling buttons");
22077 this.buttons.each( function(item){
22078 if(item.cmd != 'pencil'){
22084 Roo.log("enabling buttons");
22085 if(this.editorcore.initialized){
22086 this.buttons.each( function(item){
22092 Roo.log("calling toggole on editor");
22093 // tell the editor that it's been pressed..
22094 this.editor.toggleSourceEdit(sourceEditMode);
22104 * @class Roo.bootstrap.Table.AbstractSelectionModel
22105 * @extends Roo.util.Observable
22106 * Abstract base class for grid SelectionModels. It provides the interface that should be
22107 * implemented by descendant classes. This class should not be directly instantiated.
22110 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22111 this.locked = false;
22112 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22116 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22117 /** @ignore Called by the grid automatically. Do not call directly. */
22118 init : function(grid){
22124 * Locks the selections.
22127 this.locked = true;
22131 * Unlocks the selections.
22133 unlock : function(){
22134 this.locked = false;
22138 * Returns true if the selections are locked.
22139 * @return {Boolean}
22141 isLocked : function(){
22142 return this.locked;
22146 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22147 * @class Roo.bootstrap.Table.RowSelectionModel
22148 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22149 * It supports multiple selections and keyboard selection/navigation.
22151 * @param {Object} config
22154 Roo.bootstrap.Table.RowSelectionModel = function(config){
22155 Roo.apply(this, config);
22156 this.selections = new Roo.util.MixedCollection(false, function(o){
22161 this.lastActive = false;
22165 * @event selectionchange
22166 * Fires when the selection changes
22167 * @param {SelectionModel} this
22169 "selectionchange" : true,
22171 * @event afterselectionchange
22172 * Fires after the selection changes (eg. by key press or clicking)
22173 * @param {SelectionModel} this
22175 "afterselectionchange" : true,
22177 * @event beforerowselect
22178 * Fires when a row is selected being selected, return false to cancel.
22179 * @param {SelectionModel} this
22180 * @param {Number} rowIndex The selected index
22181 * @param {Boolean} keepExisting False if other selections will be cleared
22183 "beforerowselect" : true,
22186 * Fires when a row is selected.
22187 * @param {SelectionModel} this
22188 * @param {Number} rowIndex The selected index
22189 * @param {Roo.data.Record} r The record
22191 "rowselect" : true,
22193 * @event rowdeselect
22194 * Fires when a row is deselected.
22195 * @param {SelectionModel} this
22196 * @param {Number} rowIndex The selected index
22198 "rowdeselect" : true
22200 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22201 this.locked = false;
22204 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22206 * @cfg {Boolean} singleSelect
22207 * True to allow selection of only one row at a time (defaults to false)
22209 singleSelect : false,
22212 initEvents : function(){
22214 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22215 this.grid.on("mousedown", this.handleMouseDown, this);
22216 }else{ // allow click to work like normal
22217 this.grid.on("rowclick", this.handleDragableRowClick, this);
22220 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22221 "up" : function(e){
22223 this.selectPrevious(e.shiftKey);
22224 }else if(this.last !== false && this.lastActive !== false){
22225 var last = this.last;
22226 this.selectRange(this.last, this.lastActive-1);
22227 this.grid.getView().focusRow(this.lastActive);
22228 if(last !== false){
22232 this.selectFirstRow();
22234 this.fireEvent("afterselectionchange", this);
22236 "down" : function(e){
22238 this.selectNext(e.shiftKey);
22239 }else if(this.last !== false && this.lastActive !== false){
22240 var last = this.last;
22241 this.selectRange(this.last, this.lastActive+1);
22242 this.grid.getView().focusRow(this.lastActive);
22243 if(last !== false){
22247 this.selectFirstRow();
22249 this.fireEvent("afterselectionchange", this);
22254 var view = this.grid.view;
22255 view.on("refresh", this.onRefresh, this);
22256 view.on("rowupdated", this.onRowUpdated, this);
22257 view.on("rowremoved", this.onRemove, this);
22261 onRefresh : function(){
22262 var ds = this.grid.dataSource, i, v = this.grid.view;
22263 var s = this.selections;
22264 s.each(function(r){
22265 if((i = ds.indexOfId(r.id)) != -1){
22274 onRemove : function(v, index, r){
22275 this.selections.remove(r);
22279 onRowUpdated : function(v, index, r){
22280 if(this.isSelected(r)){
22281 v.onRowSelect(index);
22287 * @param {Array} records The records to select
22288 * @param {Boolean} keepExisting (optional) True to keep existing selections
22290 selectRecords : function(records, keepExisting){
22292 this.clearSelections();
22294 var ds = this.grid.dataSource;
22295 for(var i = 0, len = records.length; i < len; i++){
22296 this.selectRow(ds.indexOf(records[i]), true);
22301 * Gets the number of selected rows.
22304 getCount : function(){
22305 return this.selections.length;
22309 * Selects the first row in the grid.
22311 selectFirstRow : function(){
22316 * Select the last row.
22317 * @param {Boolean} keepExisting (optional) True to keep existing selections
22319 selectLastRow : function(keepExisting){
22320 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22324 * Selects the row immediately following the last selected row.
22325 * @param {Boolean} keepExisting (optional) True to keep existing selections
22327 selectNext : function(keepExisting){
22328 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22329 this.selectRow(this.last+1, keepExisting);
22330 this.grid.getView().focusRow(this.last);
22335 * Selects the row that precedes the last selected row.
22336 * @param {Boolean} keepExisting (optional) True to keep existing selections
22338 selectPrevious : function(keepExisting){
22340 this.selectRow(this.last-1, keepExisting);
22341 this.grid.getView().focusRow(this.last);
22346 * Returns the selected records
22347 * @return {Array} Array of selected records
22349 getSelections : function(){
22350 return [].concat(this.selections.items);
22354 * Returns the first selected record.
22357 getSelected : function(){
22358 return this.selections.itemAt(0);
22363 * Clears all selections.
22365 clearSelections : function(fast){
22370 var ds = this.grid.dataSource;
22371 var s = this.selections;
22372 s.each(function(r){
22373 this.deselectRow(ds.indexOfId(r.id));
22377 this.selections.clear();
22384 * Selects all rows.
22386 selectAll : function(){
22390 this.selections.clear();
22391 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22392 this.selectRow(i, true);
22397 * Returns True if there is a selection.
22398 * @return {Boolean}
22400 hasSelection : function(){
22401 return this.selections.length > 0;
22405 * Returns True if the specified row is selected.
22406 * @param {Number/Record} record The record or index of the record to check
22407 * @return {Boolean}
22409 isSelected : function(index){
22410 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22411 return (r && this.selections.key(r.id) ? true : false);
22415 * Returns True if the specified record id is selected.
22416 * @param {String} id The id of record to check
22417 * @return {Boolean}
22419 isIdSelected : function(id){
22420 return (this.selections.key(id) ? true : false);
22424 handleMouseDown : function(e, t){
22425 var view = this.grid.getView(), rowIndex;
22426 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22429 if(e.shiftKey && this.last !== false){
22430 var last = this.last;
22431 this.selectRange(last, rowIndex, e.ctrlKey);
22432 this.last = last; // reset the last
22433 view.focusRow(rowIndex);
22435 var isSelected = this.isSelected(rowIndex);
22436 if(e.button !== 0 && isSelected){
22437 view.focusRow(rowIndex);
22438 }else if(e.ctrlKey && isSelected){
22439 this.deselectRow(rowIndex);
22440 }else if(!isSelected){
22441 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22442 view.focusRow(rowIndex);
22445 this.fireEvent("afterselectionchange", this);
22448 handleDragableRowClick : function(grid, rowIndex, e)
22450 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22451 this.selectRow(rowIndex, false);
22452 grid.view.focusRow(rowIndex);
22453 this.fireEvent("afterselectionchange", this);
22458 * Selects multiple rows.
22459 * @param {Array} rows Array of the indexes of the row to select
22460 * @param {Boolean} keepExisting (optional) True to keep existing selections
22462 selectRows : function(rows, keepExisting){
22464 this.clearSelections();
22466 for(var i = 0, len = rows.length; i < len; i++){
22467 this.selectRow(rows[i], true);
22472 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22473 * @param {Number} startRow The index of the first row in the range
22474 * @param {Number} endRow The index of the last row in the range
22475 * @param {Boolean} keepExisting (optional) True to retain existing selections
22477 selectRange : function(startRow, endRow, keepExisting){
22482 this.clearSelections();
22484 if(startRow <= endRow){
22485 for(var i = startRow; i <= endRow; i++){
22486 this.selectRow(i, true);
22489 for(var i = startRow; i >= endRow; i--){
22490 this.selectRow(i, true);
22496 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22497 * @param {Number} startRow The index of the first row in the range
22498 * @param {Number} endRow The index of the last row in the range
22500 deselectRange : function(startRow, endRow, preventViewNotify){
22504 for(var i = startRow; i <= endRow; i++){
22505 this.deselectRow(i, preventViewNotify);
22511 * @param {Number} row The index of the row to select
22512 * @param {Boolean} keepExisting (optional) True to keep existing selections
22514 selectRow : function(index, keepExisting, preventViewNotify){
22515 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22518 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22519 if(!keepExisting || this.singleSelect){
22520 this.clearSelections();
22522 var r = this.grid.dataSource.getAt(index);
22523 this.selections.add(r);
22524 this.last = this.lastActive = index;
22525 if(!preventViewNotify){
22526 this.grid.getView().onRowSelect(index);
22528 this.fireEvent("rowselect", this, index, r);
22529 this.fireEvent("selectionchange", this);
22535 * @param {Number} row The index of the row to deselect
22537 deselectRow : function(index, preventViewNotify){
22541 if(this.last == index){
22544 if(this.lastActive == index){
22545 this.lastActive = false;
22547 var r = this.grid.dataSource.getAt(index);
22548 this.selections.remove(r);
22549 if(!preventViewNotify){
22550 this.grid.getView().onRowDeselect(index);
22552 this.fireEvent("rowdeselect", this, index);
22553 this.fireEvent("selectionchange", this);
22557 restoreLast : function(){
22559 this.last = this._last;
22564 acceptsNav : function(row, col, cm){
22565 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22569 onEditorKey : function(field, e){
22570 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22575 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22577 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22579 }else if(k == e.ENTER && !e.ctrlKey){
22583 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22585 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22587 }else if(k == e.ESC){
22591 g.startEditing(newCell[0], newCell[1]);
22596 * Ext JS Library 1.1.1
22597 * Copyright(c) 2006-2007, Ext JS, LLC.
22599 * Originally Released Under LGPL - original licence link has changed is not relivant.
22602 * <script type="text/javascript">
22606 * @class Roo.bootstrap.PagingToolbar
22607 * @extends Roo.bootstrap.NavSimplebar
22608 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22610 * Create a new PagingToolbar
22611 * @param {Object} config The config object
22612 * @param {Roo.data.Store} store
22614 Roo.bootstrap.PagingToolbar = function(config)
22616 // old args format still supported... - xtype is prefered..
22617 // created from xtype...
22619 this.ds = config.dataSource;
22621 if (config.store && !this.ds) {
22622 this.store= Roo.factory(config.store, Roo.data);
22623 this.ds = this.store;
22624 this.ds.xmodule = this.xmodule || false;
22627 this.toolbarItems = [];
22628 if (config.items) {
22629 this.toolbarItems = config.items;
22632 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22637 this.bind(this.ds);
22640 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22644 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22646 * @cfg {Roo.data.Store} dataSource
22647 * The underlying data store providing the paged data
22650 * @cfg {String/HTMLElement/Element} container
22651 * container The id or element that will contain the toolbar
22654 * @cfg {Boolean} displayInfo
22655 * True to display the displayMsg (defaults to false)
22658 * @cfg {Number} pageSize
22659 * The number of records to display per page (defaults to 20)
22663 * @cfg {String} displayMsg
22664 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22666 displayMsg : 'Displaying {0} - {1} of {2}',
22668 * @cfg {String} emptyMsg
22669 * The message to display when no records are found (defaults to "No data to display")
22671 emptyMsg : 'No data to display',
22673 * Customizable piece of the default paging text (defaults to "Page")
22676 beforePageText : "Page",
22678 * Customizable piece of the default paging text (defaults to "of %0")
22681 afterPageText : "of {0}",
22683 * Customizable piece of the default paging text (defaults to "First Page")
22686 firstText : "First Page",
22688 * Customizable piece of the default paging text (defaults to "Previous Page")
22691 prevText : "Previous Page",
22693 * Customizable piece of the default paging text (defaults to "Next Page")
22696 nextText : "Next Page",
22698 * Customizable piece of the default paging text (defaults to "Last Page")
22701 lastText : "Last Page",
22703 * Customizable piece of the default paging text (defaults to "Refresh")
22706 refreshText : "Refresh",
22710 onRender : function(ct, position)
22712 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22713 this.navgroup.parentId = this.id;
22714 this.navgroup.onRender(this.el, null);
22715 // add the buttons to the navgroup
22717 if(this.displayInfo){
22718 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22719 this.displayEl = this.el.select('.x-paging-info', true).first();
22720 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22721 // this.displayEl = navel.el.select('span',true).first();
22727 Roo.each(_this.buttons, function(e){ // this might need to use render????
22728 Roo.factory(e).onRender(_this.el, null);
22732 Roo.each(_this.toolbarItems, function(e) {
22733 _this.navgroup.addItem(e);
22737 this.first = this.navgroup.addItem({
22738 tooltip: this.firstText,
22740 icon : 'fa fa-backward',
22742 preventDefault: true,
22743 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22746 this.prev = this.navgroup.addItem({
22747 tooltip: this.prevText,
22749 icon : 'fa fa-step-backward',
22751 preventDefault: true,
22752 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22754 //this.addSeparator();
22757 var field = this.navgroup.addItem( {
22759 cls : 'x-paging-position',
22761 html : this.beforePageText +
22762 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22763 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22766 this.field = field.el.select('input', true).first();
22767 this.field.on("keydown", this.onPagingKeydown, this);
22768 this.field.on("focus", function(){this.dom.select();});
22771 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22772 //this.field.setHeight(18);
22773 //this.addSeparator();
22774 this.next = this.navgroup.addItem({
22775 tooltip: this.nextText,
22777 html : ' <i class="fa fa-step-forward">',
22779 preventDefault: true,
22780 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22782 this.last = this.navgroup.addItem({
22783 tooltip: this.lastText,
22784 icon : 'fa fa-forward',
22787 preventDefault: true,
22788 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22790 //this.addSeparator();
22791 this.loading = this.navgroup.addItem({
22792 tooltip: this.refreshText,
22793 icon: 'fa fa-refresh',
22794 preventDefault: true,
22795 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22801 updateInfo : function(){
22802 if(this.displayEl){
22803 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22804 var msg = count == 0 ?
22808 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22810 this.displayEl.update(msg);
22815 onLoad : function(ds, r, o){
22816 this.cursor = o.params ? o.params.start : 0;
22817 var d = this.getPageData(),
22821 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22822 this.field.dom.value = ap;
22823 this.first.setDisabled(ap == 1);
22824 this.prev.setDisabled(ap == 1);
22825 this.next.setDisabled(ap == ps);
22826 this.last.setDisabled(ap == ps);
22827 this.loading.enable();
22832 getPageData : function(){
22833 var total = this.ds.getTotalCount();
22836 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22837 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22842 onLoadError : function(){
22843 this.loading.enable();
22847 onPagingKeydown : function(e){
22848 var k = e.getKey();
22849 var d = this.getPageData();
22851 var v = this.field.dom.value, pageNum;
22852 if(!v || isNaN(pageNum = parseInt(v, 10))){
22853 this.field.dom.value = d.activePage;
22856 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22857 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22860 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))
22862 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22863 this.field.dom.value = pageNum;
22864 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22867 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22869 var v = this.field.dom.value, pageNum;
22870 var increment = (e.shiftKey) ? 10 : 1;
22871 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22874 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22875 this.field.dom.value = d.activePage;
22878 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22880 this.field.dom.value = parseInt(v, 10) + increment;
22881 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22882 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22889 beforeLoad : function(){
22891 this.loading.disable();
22896 onClick : function(which){
22905 ds.load({params:{start: 0, limit: this.pageSize}});
22908 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22911 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22914 var total = ds.getTotalCount();
22915 var extra = total % this.pageSize;
22916 var lastStart = extra ? (total - extra) : total-this.pageSize;
22917 ds.load({params:{start: lastStart, limit: this.pageSize}});
22920 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22926 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22927 * @param {Roo.data.Store} store The data store to unbind
22929 unbind : function(ds){
22930 ds.un("beforeload", this.beforeLoad, this);
22931 ds.un("load", this.onLoad, this);
22932 ds.un("loadexception", this.onLoadError, this);
22933 ds.un("remove", this.updateInfo, this);
22934 ds.un("add", this.updateInfo, this);
22935 this.ds = undefined;
22939 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22940 * @param {Roo.data.Store} store The data store to bind
22942 bind : function(ds){
22943 ds.on("beforeload", this.beforeLoad, this);
22944 ds.on("load", this.onLoad, this);
22945 ds.on("loadexception", this.onLoadError, this);
22946 ds.on("remove", this.updateInfo, this);
22947 ds.on("add", this.updateInfo, this);
22958 * @class Roo.bootstrap.MessageBar
22959 * @extends Roo.bootstrap.Component
22960 * Bootstrap MessageBar class
22961 * @cfg {String} html contents of the MessageBar
22962 * @cfg {String} weight (info | success | warning | danger) default info
22963 * @cfg {String} beforeClass insert the bar before the given class
22964 * @cfg {Boolean} closable (true | false) default false
22965 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22968 * Create a new Element
22969 * @param {Object} config The config object
22972 Roo.bootstrap.MessageBar = function(config){
22973 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22976 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22982 beforeClass: 'bootstrap-sticky-wrap',
22984 getAutoCreate : function(){
22988 cls: 'alert alert-dismissable alert-' + this.weight,
22993 html: this.html || ''
22999 cfg.cls += ' alert-messages-fixed';
23013 onRender : function(ct, position)
23015 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23018 var cfg = Roo.apply({}, this.getAutoCreate());
23022 cfg.cls += ' ' + this.cls;
23025 cfg.style = this.style;
23027 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23029 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23032 this.el.select('>button.close').on('click', this.hide, this);
23038 if (!this.rendered) {
23044 this.fireEvent('show', this);
23050 if (!this.rendered) {
23056 this.fireEvent('hide', this);
23059 update : function()
23061 // var e = this.el.dom.firstChild;
23063 // if(this.closable){
23064 // e = e.nextSibling;
23067 // e.data = this.html || '';
23069 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23085 * @class Roo.bootstrap.Graph
23086 * @extends Roo.bootstrap.Component
23087 * Bootstrap Graph class
23091 @cfg {String} graphtype bar | vbar | pie
23092 @cfg {number} g_x coodinator | centre x (pie)
23093 @cfg {number} g_y coodinator | centre y (pie)
23094 @cfg {number} g_r radius (pie)
23095 @cfg {number} g_height height of the chart (respected by all elements in the set)
23096 @cfg {number} g_width width of the chart (respected by all elements in the set)
23097 @cfg {Object} title The title of the chart
23100 -opts (object) options for the chart
23102 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23103 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23105 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.
23106 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23108 o stretch (boolean)
23110 -opts (object) options for the pie
23113 o startAngle (number)
23114 o endAngle (number)
23118 * Create a new Input
23119 * @param {Object} config The config object
23122 Roo.bootstrap.Graph = function(config){
23123 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23129 * The img click event for the img.
23130 * @param {Roo.EventObject} e
23136 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23147 //g_colors: this.colors,
23154 getAutoCreate : function(){
23165 onRender : function(ct,position){
23168 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23170 if (typeof(Raphael) == 'undefined') {
23171 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23175 this.raphael = Raphael(this.el.dom);
23177 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23178 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23179 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23180 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23182 r.text(160, 10, "Single Series Chart").attr(txtattr);
23183 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23184 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23185 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23187 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23188 r.barchart(330, 10, 300, 220, data1);
23189 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23190 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23193 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23194 // r.barchart(30, 30, 560, 250, xdata, {
23195 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23196 // axis : "0 0 1 1",
23197 // axisxlabels : xdata
23198 // //yvalues : cols,
23201 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23203 // this.load(null,xdata,{
23204 // axis : "0 0 1 1",
23205 // axisxlabels : xdata
23210 load : function(graphtype,xdata,opts)
23212 this.raphael.clear();
23214 graphtype = this.graphtype;
23219 var r = this.raphael,
23220 fin = function () {
23221 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23223 fout = function () {
23224 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23226 pfin = function() {
23227 this.sector.stop();
23228 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23231 this.label[0].stop();
23232 this.label[0].attr({ r: 7.5 });
23233 this.label[1].attr({ "font-weight": 800 });
23236 pfout = function() {
23237 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23240 this.label[0].animate({ r: 5 }, 500, "bounce");
23241 this.label[1].attr({ "font-weight": 400 });
23247 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23250 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23253 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23254 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23256 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23263 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23268 setTitle: function(o)
23273 initEvents: function() {
23276 this.el.on('click', this.onClick, this);
23280 onClick : function(e)
23282 Roo.log('img onclick');
23283 this.fireEvent('click', this, e);
23295 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23298 * @class Roo.bootstrap.dash.NumberBox
23299 * @extends Roo.bootstrap.Component
23300 * Bootstrap NumberBox class
23301 * @cfg {String} headline Box headline
23302 * @cfg {String} content Box content
23303 * @cfg {String} icon Box icon
23304 * @cfg {String} footer Footer text
23305 * @cfg {String} fhref Footer href
23308 * Create a new NumberBox
23309 * @param {Object} config The config object
23313 Roo.bootstrap.dash.NumberBox = function(config){
23314 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23318 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23327 getAutoCreate : function(){
23331 cls : 'small-box ',
23339 cls : 'roo-headline',
23340 html : this.headline
23344 cls : 'roo-content',
23345 html : this.content
23359 cls : 'ion ' + this.icon
23368 cls : 'small-box-footer',
23369 href : this.fhref || '#',
23373 cfg.cn.push(footer);
23380 onRender : function(ct,position){
23381 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23388 setHeadline: function (value)
23390 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23393 setFooter: function (value, href)
23395 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23398 this.el.select('a.small-box-footer',true).first().attr('href', href);
23403 setContent: function (value)
23405 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23408 initEvents: function()
23422 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23425 * @class Roo.bootstrap.dash.TabBox
23426 * @extends Roo.bootstrap.Component
23427 * Bootstrap TabBox class
23428 * @cfg {String} title Title of the TabBox
23429 * @cfg {String} icon Icon of the TabBox
23430 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23431 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23434 * Create a new TabBox
23435 * @param {Object} config The config object
23439 Roo.bootstrap.dash.TabBox = function(config){
23440 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23445 * When a pane is added
23446 * @param {Roo.bootstrap.dash.TabPane} pane
23450 * @event activatepane
23451 * When a pane is activated
23452 * @param {Roo.bootstrap.dash.TabPane} pane
23454 "activatepane" : true
23462 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23467 tabScrollable : false,
23469 getChildContainer : function()
23471 return this.el.select('.tab-content', true).first();
23474 getAutoCreate : function(){
23478 cls: 'pull-left header',
23486 cls: 'fa ' + this.icon
23492 cls: 'nav nav-tabs pull-right',
23498 if(this.tabScrollable){
23505 cls: 'nav nav-tabs pull-right',
23516 cls: 'nav-tabs-custom',
23521 cls: 'tab-content no-padding',
23529 initEvents : function()
23531 //Roo.log('add add pane handler');
23532 this.on('addpane', this.onAddPane, this);
23535 * Updates the box title
23536 * @param {String} html to set the title to.
23538 setTitle : function(value)
23540 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23542 onAddPane : function(pane)
23544 this.panes.push(pane);
23545 //Roo.log('addpane');
23547 // tabs are rendere left to right..
23548 if(!this.showtabs){
23552 var ctr = this.el.select('.nav-tabs', true).first();
23555 var existing = ctr.select('.nav-tab',true);
23556 var qty = existing.getCount();;
23559 var tab = ctr.createChild({
23561 cls : 'nav-tab' + (qty ? '' : ' active'),
23569 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23572 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23574 pane.el.addClass('active');
23579 onTabClick : function(ev,un,ob,pane)
23581 //Roo.log('tab - prev default');
23582 ev.preventDefault();
23585 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23586 pane.tab.addClass('active');
23587 //Roo.log(pane.title);
23588 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23589 // technically we should have a deactivate event.. but maybe add later.
23590 // and it should not de-activate the selected tab...
23591 this.fireEvent('activatepane', pane);
23592 pane.el.addClass('active');
23593 pane.fireEvent('activate');
23598 getActivePane : function()
23601 Roo.each(this.panes, function(p) {
23602 if(p.el.hasClass('active')){
23623 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23625 * @class Roo.bootstrap.TabPane
23626 * @extends Roo.bootstrap.Component
23627 * Bootstrap TabPane class
23628 * @cfg {Boolean} active (false | true) Default false
23629 * @cfg {String} title title of panel
23633 * Create a new TabPane
23634 * @param {Object} config The config object
23637 Roo.bootstrap.dash.TabPane = function(config){
23638 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23644 * When a pane is activated
23645 * @param {Roo.bootstrap.dash.TabPane} pane
23652 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23657 // the tabBox that this is attached to.
23660 getAutoCreate : function()
23668 cfg.cls += ' active';
23673 initEvents : function()
23675 //Roo.log('trigger add pane handler');
23676 this.parent().fireEvent('addpane', this)
23680 * Updates the tab title
23681 * @param {String} html to set the title to.
23683 setTitle: function(str)
23689 this.tab.select('a', true).first().dom.innerHTML = str;
23706 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23709 * @class Roo.bootstrap.menu.Menu
23710 * @extends Roo.bootstrap.Component
23711 * Bootstrap Menu class - container for Menu
23712 * @cfg {String} html Text of the menu
23713 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23714 * @cfg {String} icon Font awesome icon
23715 * @cfg {String} pos Menu align to (top | bottom) default bottom
23719 * Create a new Menu
23720 * @param {Object} config The config object
23724 Roo.bootstrap.menu.Menu = function(config){
23725 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23729 * @event beforeshow
23730 * Fires before this menu is displayed
23731 * @param {Roo.bootstrap.menu.Menu} this
23735 * @event beforehide
23736 * Fires before this menu is hidden
23737 * @param {Roo.bootstrap.menu.Menu} this
23742 * Fires after this menu is displayed
23743 * @param {Roo.bootstrap.menu.Menu} this
23748 * Fires after this menu is hidden
23749 * @param {Roo.bootstrap.menu.Menu} this
23754 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23755 * @param {Roo.bootstrap.menu.Menu} this
23756 * @param {Roo.EventObject} e
23763 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23767 weight : 'default',
23772 getChildContainer : function() {
23773 if(this.isSubMenu){
23777 return this.el.select('ul.dropdown-menu', true).first();
23780 getAutoCreate : function()
23785 cls : 'roo-menu-text',
23793 cls : 'fa ' + this.icon
23804 cls : 'dropdown-button btn btn-' + this.weight,
23809 cls : 'dropdown-toggle btn btn-' + this.weight,
23819 cls : 'dropdown-menu'
23825 if(this.pos == 'top'){
23826 cfg.cls += ' dropup';
23829 if(this.isSubMenu){
23832 cls : 'dropdown-menu'
23839 onRender : function(ct, position)
23841 this.isSubMenu = ct.hasClass('dropdown-submenu');
23843 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23846 initEvents : function()
23848 if(this.isSubMenu){
23852 this.hidden = true;
23854 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23855 this.triggerEl.on('click', this.onTriggerPress, this);
23857 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23858 this.buttonEl.on('click', this.onClick, this);
23864 if(this.isSubMenu){
23868 return this.el.select('ul.dropdown-menu', true).first();
23871 onClick : function(e)
23873 this.fireEvent("click", this, e);
23876 onTriggerPress : function(e)
23878 if (this.isVisible()) {
23885 isVisible : function(){
23886 return !this.hidden;
23891 this.fireEvent("beforeshow", this);
23893 this.hidden = false;
23894 this.el.addClass('open');
23896 Roo.get(document).on("mouseup", this.onMouseUp, this);
23898 this.fireEvent("show", this);
23905 this.fireEvent("beforehide", this);
23907 this.hidden = true;
23908 this.el.removeClass('open');
23910 Roo.get(document).un("mouseup", this.onMouseUp);
23912 this.fireEvent("hide", this);
23915 onMouseUp : function()
23929 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23932 * @class Roo.bootstrap.menu.Item
23933 * @extends Roo.bootstrap.Component
23934 * Bootstrap MenuItem class
23935 * @cfg {Boolean} submenu (true | false) default false
23936 * @cfg {String} html text of the item
23937 * @cfg {String} href the link
23938 * @cfg {Boolean} disable (true | false) default false
23939 * @cfg {Boolean} preventDefault (true | false) default true
23940 * @cfg {String} icon Font awesome icon
23941 * @cfg {String} pos Submenu align to (left | right) default right
23945 * Create a new Item
23946 * @param {Object} config The config object
23950 Roo.bootstrap.menu.Item = function(config){
23951 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23955 * Fires when the mouse is hovering over this menu
23956 * @param {Roo.bootstrap.menu.Item} this
23957 * @param {Roo.EventObject} e
23962 * Fires when the mouse exits this menu
23963 * @param {Roo.bootstrap.menu.Item} this
23964 * @param {Roo.EventObject} e
23970 * The raw click event for the entire grid.
23971 * @param {Roo.EventObject} e
23977 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23982 preventDefault: true,
23987 getAutoCreate : function()
23992 cls : 'roo-menu-item-text',
24000 cls : 'fa ' + this.icon
24009 href : this.href || '#',
24016 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24020 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24022 if(this.pos == 'left'){
24023 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24030 initEvents : function()
24032 this.el.on('mouseover', this.onMouseOver, this);
24033 this.el.on('mouseout', this.onMouseOut, this);
24035 this.el.select('a', true).first().on('click', this.onClick, this);
24039 onClick : function(e)
24041 if(this.preventDefault){
24042 e.preventDefault();
24045 this.fireEvent("click", this, e);
24048 onMouseOver : function(e)
24050 if(this.submenu && this.pos == 'left'){
24051 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24054 this.fireEvent("mouseover", this, e);
24057 onMouseOut : function(e)
24059 this.fireEvent("mouseout", this, e);
24071 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24074 * @class Roo.bootstrap.menu.Separator
24075 * @extends Roo.bootstrap.Component
24076 * Bootstrap Separator class
24079 * Create a new Separator
24080 * @param {Object} config The config object
24084 Roo.bootstrap.menu.Separator = function(config){
24085 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24088 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24090 getAutoCreate : function(){
24111 * @class Roo.bootstrap.Tooltip
24112 * Bootstrap Tooltip class
24113 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24114 * to determine which dom element triggers the tooltip.
24116 * It needs to add support for additional attributes like tooltip-position
24119 * Create a new Toolti
24120 * @param {Object} config The config object
24123 Roo.bootstrap.Tooltip = function(config){
24124 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24127 Roo.apply(Roo.bootstrap.Tooltip, {
24129 * @function init initialize tooltip monitoring.
24133 currentTip : false,
24134 currentRegion : false,
24140 Roo.get(document).on('mouseover', this.enter ,this);
24141 Roo.get(document).on('mouseout', this.leave, this);
24144 this.currentTip = new Roo.bootstrap.Tooltip();
24147 enter : function(ev)
24149 var dom = ev.getTarget();
24151 //Roo.log(['enter',dom]);
24152 var el = Roo.fly(dom);
24153 if (this.currentEl) {
24155 //Roo.log(this.currentEl);
24156 //Roo.log(this.currentEl.contains(dom));
24157 if (this.currentEl == el) {
24160 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24166 if (this.currentTip.el) {
24167 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24171 if(!el || el.dom == document){
24177 // you can not look for children, as if el is the body.. then everythign is the child..
24178 if (!el.attr('tooltip')) { //
24179 if (!el.select("[tooltip]").elements.length) {
24182 // is the mouse over this child...?
24183 bindEl = el.select("[tooltip]").first();
24184 var xy = ev.getXY();
24185 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24186 //Roo.log("not in region.");
24189 //Roo.log("child element over..");
24192 this.currentEl = bindEl;
24193 this.currentTip.bind(bindEl);
24194 this.currentRegion = Roo.lib.Region.getRegion(dom);
24195 this.currentTip.enter();
24198 leave : function(ev)
24200 var dom = ev.getTarget();
24201 //Roo.log(['leave',dom]);
24202 if (!this.currentEl) {
24207 if (dom != this.currentEl.dom) {
24210 var xy = ev.getXY();
24211 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24214 // only activate leave if mouse cursor is outside... bounding box..
24219 if (this.currentTip) {
24220 this.currentTip.leave();
24222 //Roo.log('clear currentEl');
24223 this.currentEl = false;
24228 'left' : ['r-l', [-2,0], 'right'],
24229 'right' : ['l-r', [2,0], 'left'],
24230 'bottom' : ['t-b', [0,2], 'top'],
24231 'top' : [ 'b-t', [0,-2], 'bottom']
24237 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24242 delay : null, // can be { show : 300 , hide: 500}
24246 hoverState : null, //???
24248 placement : 'bottom',
24250 getAutoCreate : function(){
24257 cls : 'tooltip-arrow'
24260 cls : 'tooltip-inner'
24267 bind : function(el)
24273 enter : function () {
24275 if (this.timeout != null) {
24276 clearTimeout(this.timeout);
24279 this.hoverState = 'in';
24280 //Roo.log("enter - show");
24281 if (!this.delay || !this.delay.show) {
24286 this.timeout = setTimeout(function () {
24287 if (_t.hoverState == 'in') {
24290 }, this.delay.show);
24294 clearTimeout(this.timeout);
24296 this.hoverState = 'out';
24297 if (!this.delay || !this.delay.hide) {
24303 this.timeout = setTimeout(function () {
24304 //Roo.log("leave - timeout");
24306 if (_t.hoverState == 'out') {
24308 Roo.bootstrap.Tooltip.currentEl = false;
24316 this.render(document.body);
24319 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24321 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24323 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24325 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24327 var placement = typeof this.placement == 'function' ?
24328 this.placement.call(this, this.el, on_el) :
24331 var autoToken = /\s?auto?\s?/i;
24332 var autoPlace = autoToken.test(placement);
24334 placement = placement.replace(autoToken, '') || 'top';
24338 //this.el.setXY([0,0]);
24340 //this.el.dom.style.display='block';
24342 //this.el.appendTo(on_el);
24344 var p = this.getPosition();
24345 var box = this.el.getBox();
24351 var align = Roo.bootstrap.Tooltip.alignment[placement];
24353 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24355 if(placement == 'top' || placement == 'bottom'){
24357 placement = 'right';
24360 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24361 placement = 'left';
24364 var scroll = Roo.select('body', true).first().getScroll();
24366 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24372 align = Roo.bootstrap.Tooltip.alignment[placement];
24374 this.el.alignTo(this.bindEl, align[0],align[1]);
24375 //var arrow = this.el.select('.arrow',true).first();
24376 //arrow.set(align[2],
24378 this.el.addClass(placement);
24380 this.el.addClass('in fade');
24382 this.hoverState = null;
24384 if (this.el.hasClass('fade')) {
24395 //this.el.setXY([0,0]);
24396 this.el.removeClass('in');
24412 * @class Roo.bootstrap.LocationPicker
24413 * @extends Roo.bootstrap.Component
24414 * Bootstrap LocationPicker class
24415 * @cfg {Number} latitude Position when init default 0
24416 * @cfg {Number} longitude Position when init default 0
24417 * @cfg {Number} zoom default 15
24418 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24419 * @cfg {Boolean} mapTypeControl default false
24420 * @cfg {Boolean} disableDoubleClickZoom default false
24421 * @cfg {Boolean} scrollwheel default true
24422 * @cfg {Boolean} streetViewControl default false
24423 * @cfg {Number} radius default 0
24424 * @cfg {String} locationName
24425 * @cfg {Boolean} draggable default true
24426 * @cfg {Boolean} enableAutocomplete default false
24427 * @cfg {Boolean} enableReverseGeocode default true
24428 * @cfg {String} markerTitle
24431 * Create a new LocationPicker
24432 * @param {Object} config The config object
24436 Roo.bootstrap.LocationPicker = function(config){
24438 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24443 * Fires when the picker initialized.
24444 * @param {Roo.bootstrap.LocationPicker} this
24445 * @param {Google Location} location
24449 * @event positionchanged
24450 * Fires when the picker position changed.
24451 * @param {Roo.bootstrap.LocationPicker} this
24452 * @param {Google Location} location
24454 positionchanged : true,
24457 * Fires when the map resize.
24458 * @param {Roo.bootstrap.LocationPicker} this
24463 * Fires when the map show.
24464 * @param {Roo.bootstrap.LocationPicker} this
24469 * Fires when the map hide.
24470 * @param {Roo.bootstrap.LocationPicker} this
24475 * Fires when click the map.
24476 * @param {Roo.bootstrap.LocationPicker} this
24477 * @param {Map event} e
24481 * @event mapRightClick
24482 * Fires when right click the map.
24483 * @param {Roo.bootstrap.LocationPicker} this
24484 * @param {Map event} e
24486 mapRightClick : true,
24488 * @event markerClick
24489 * Fires when click the marker.
24490 * @param {Roo.bootstrap.LocationPicker} this
24491 * @param {Map event} e
24493 markerClick : true,
24495 * @event markerRightClick
24496 * Fires when right click the marker.
24497 * @param {Roo.bootstrap.LocationPicker} this
24498 * @param {Map event} e
24500 markerRightClick : true,
24502 * @event OverlayViewDraw
24503 * Fires when OverlayView Draw
24504 * @param {Roo.bootstrap.LocationPicker} this
24506 OverlayViewDraw : true,
24508 * @event OverlayViewOnAdd
24509 * Fires when OverlayView Draw
24510 * @param {Roo.bootstrap.LocationPicker} this
24512 OverlayViewOnAdd : true,
24514 * @event OverlayViewOnRemove
24515 * Fires when OverlayView Draw
24516 * @param {Roo.bootstrap.LocationPicker} this
24518 OverlayViewOnRemove : true,
24520 * @event OverlayViewShow
24521 * Fires when OverlayView Draw
24522 * @param {Roo.bootstrap.LocationPicker} this
24523 * @param {Pixel} cpx
24525 OverlayViewShow : true,
24527 * @event OverlayViewHide
24528 * Fires when OverlayView Draw
24529 * @param {Roo.bootstrap.LocationPicker} this
24531 OverlayViewHide : true,
24533 * @event loadexception
24534 * Fires when load google lib failed.
24535 * @param {Roo.bootstrap.LocationPicker} this
24537 loadexception : true
24542 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24544 gMapContext: false,
24550 mapTypeControl: false,
24551 disableDoubleClickZoom: false,
24553 streetViewControl: false,
24557 enableAutocomplete: false,
24558 enableReverseGeocode: true,
24561 getAutoCreate: function()
24566 cls: 'roo-location-picker'
24572 initEvents: function(ct, position)
24574 if(!this.el.getWidth() || this.isApplied()){
24578 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24583 initial: function()
24585 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24586 this.fireEvent('loadexception', this);
24590 if(!this.mapTypeId){
24591 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24594 this.gMapContext = this.GMapContext();
24596 this.initOverlayView();
24598 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24602 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24603 _this.setPosition(_this.gMapContext.marker.position);
24606 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24607 _this.fireEvent('mapClick', this, event);
24611 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24612 _this.fireEvent('mapRightClick', this, event);
24616 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24617 _this.fireEvent('markerClick', this, event);
24621 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24622 _this.fireEvent('markerRightClick', this, event);
24626 this.setPosition(this.gMapContext.location);
24628 this.fireEvent('initial', this, this.gMapContext.location);
24631 initOverlayView: function()
24635 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24639 _this.fireEvent('OverlayViewDraw', _this);
24644 _this.fireEvent('OverlayViewOnAdd', _this);
24647 onRemove: function()
24649 _this.fireEvent('OverlayViewOnRemove', _this);
24652 show: function(cpx)
24654 _this.fireEvent('OverlayViewShow', _this, cpx);
24659 _this.fireEvent('OverlayViewHide', _this);
24665 fromLatLngToContainerPixel: function(event)
24667 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24670 isApplied: function()
24672 return this.getGmapContext() == false ? false : true;
24675 getGmapContext: function()
24677 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24680 GMapContext: function()
24682 var position = new google.maps.LatLng(this.latitude, this.longitude);
24684 var _map = new google.maps.Map(this.el.dom, {
24687 mapTypeId: this.mapTypeId,
24688 mapTypeControl: this.mapTypeControl,
24689 disableDoubleClickZoom: this.disableDoubleClickZoom,
24690 scrollwheel: this.scrollwheel,
24691 streetViewControl: this.streetViewControl,
24692 locationName: this.locationName,
24693 draggable: this.draggable,
24694 enableAutocomplete: this.enableAutocomplete,
24695 enableReverseGeocode: this.enableReverseGeocode
24698 var _marker = new google.maps.Marker({
24699 position: position,
24701 title: this.markerTitle,
24702 draggable: this.draggable
24709 location: position,
24710 radius: this.radius,
24711 locationName: this.locationName,
24712 addressComponents: {
24713 formatted_address: null,
24714 addressLine1: null,
24715 addressLine2: null,
24717 streetNumber: null,
24721 stateOrProvince: null
24724 domContainer: this.el.dom,
24725 geodecoder: new google.maps.Geocoder()
24729 drawCircle: function(center, radius, options)
24731 if (this.gMapContext.circle != null) {
24732 this.gMapContext.circle.setMap(null);
24736 options = Roo.apply({}, options, {
24737 strokeColor: "#0000FF",
24738 strokeOpacity: .35,
24740 fillColor: "#0000FF",
24744 options.map = this.gMapContext.map;
24745 options.radius = radius;
24746 options.center = center;
24747 this.gMapContext.circle = new google.maps.Circle(options);
24748 return this.gMapContext.circle;
24754 setPosition: function(location)
24756 this.gMapContext.location = location;
24757 this.gMapContext.marker.setPosition(location);
24758 this.gMapContext.map.panTo(location);
24759 this.drawCircle(location, this.gMapContext.radius, {});
24763 if (this.gMapContext.settings.enableReverseGeocode) {
24764 this.gMapContext.geodecoder.geocode({
24765 latLng: this.gMapContext.location
24766 }, function(results, status) {
24768 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24769 _this.gMapContext.locationName = results[0].formatted_address;
24770 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24772 _this.fireEvent('positionchanged', this, location);
24779 this.fireEvent('positionchanged', this, location);
24784 google.maps.event.trigger(this.gMapContext.map, "resize");
24786 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24788 this.fireEvent('resize', this);
24791 setPositionByLatLng: function(latitude, longitude)
24793 this.setPosition(new google.maps.LatLng(latitude, longitude));
24796 getCurrentPosition: function()
24799 latitude: this.gMapContext.location.lat(),
24800 longitude: this.gMapContext.location.lng()
24804 getAddressName: function()
24806 return this.gMapContext.locationName;
24809 getAddressComponents: function()
24811 return this.gMapContext.addressComponents;
24814 address_component_from_google_geocode: function(address_components)
24818 for (var i = 0; i < address_components.length; i++) {
24819 var component = address_components[i];
24820 if (component.types.indexOf("postal_code") >= 0) {
24821 result.postalCode = component.short_name;
24822 } else if (component.types.indexOf("street_number") >= 0) {
24823 result.streetNumber = component.short_name;
24824 } else if (component.types.indexOf("route") >= 0) {
24825 result.streetName = component.short_name;
24826 } else if (component.types.indexOf("neighborhood") >= 0) {
24827 result.city = component.short_name;
24828 } else if (component.types.indexOf("locality") >= 0) {
24829 result.city = component.short_name;
24830 } else if (component.types.indexOf("sublocality") >= 0) {
24831 result.district = component.short_name;
24832 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24833 result.stateOrProvince = component.short_name;
24834 } else if (component.types.indexOf("country") >= 0) {
24835 result.country = component.short_name;
24839 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24840 result.addressLine2 = "";
24844 setZoomLevel: function(zoom)
24846 this.gMapContext.map.setZoom(zoom);
24859 this.fireEvent('show', this);
24870 this.fireEvent('hide', this);
24875 Roo.apply(Roo.bootstrap.LocationPicker, {
24877 OverlayView : function(map, options)
24879 options = options || {};
24893 * @class Roo.bootstrap.Alert
24894 * @extends Roo.bootstrap.Component
24895 * Bootstrap Alert class
24896 * @cfg {String} title The title of alert
24897 * @cfg {String} html The content of alert
24898 * @cfg {String} weight ( success | info | warning | danger )
24899 * @cfg {String} faicon font-awesomeicon
24902 * Create a new alert
24903 * @param {Object} config The config object
24907 Roo.bootstrap.Alert = function(config){
24908 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24912 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24919 getAutoCreate : function()
24928 cls : 'roo-alert-icon'
24933 cls : 'roo-alert-title',
24938 cls : 'roo-alert-text',
24945 cfg.cn[0].cls += ' fa ' + this.faicon;
24949 cfg.cls += ' alert-' + this.weight;
24955 initEvents: function()
24957 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24960 setTitle : function(str)
24962 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24965 setText : function(str)
24967 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24970 setWeight : function(weight)
24973 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24976 this.weight = weight;
24978 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24981 setIcon : function(icon)
24984 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24987 this.faicon = icon;
24989 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25010 * @class Roo.bootstrap.UploadCropbox
25011 * @extends Roo.bootstrap.Component
25012 * Bootstrap UploadCropbox class
25013 * @cfg {String} emptyText show when image has been loaded
25014 * @cfg {String} rotateNotify show when image too small to rotate
25015 * @cfg {Number} errorTimeout default 3000
25016 * @cfg {Number} minWidth default 300
25017 * @cfg {Number} minHeight default 300
25018 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25019 * @cfg {Boolean} isDocument (true|false) default false
25020 * @cfg {String} url action url
25021 * @cfg {String} paramName default 'imageUpload'
25022 * @cfg {String} method default POST
25023 * @cfg {Boolean} loadMask (true|false) default true
25024 * @cfg {Boolean} loadingText default 'Loading...'
25027 * Create a new UploadCropbox
25028 * @param {Object} config The config object
25031 Roo.bootstrap.UploadCropbox = function(config){
25032 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25036 * @event beforeselectfile
25037 * Fire before select file
25038 * @param {Roo.bootstrap.UploadCropbox} this
25040 "beforeselectfile" : true,
25043 * Fire after initEvent
25044 * @param {Roo.bootstrap.UploadCropbox} this
25049 * Fire after initEvent
25050 * @param {Roo.bootstrap.UploadCropbox} this
25051 * @param {String} data
25056 * Fire when preparing the file data
25057 * @param {Roo.bootstrap.UploadCropbox} this
25058 * @param {Object} file
25063 * Fire when get exception
25064 * @param {Roo.bootstrap.UploadCropbox} this
25065 * @param {XMLHttpRequest} xhr
25067 "exception" : true,
25069 * @event beforeloadcanvas
25070 * Fire before load the canvas
25071 * @param {Roo.bootstrap.UploadCropbox} this
25072 * @param {String} src
25074 "beforeloadcanvas" : true,
25077 * Fire when trash image
25078 * @param {Roo.bootstrap.UploadCropbox} this
25083 * Fire when download the image
25084 * @param {Roo.bootstrap.UploadCropbox} this
25088 * @event footerbuttonclick
25089 * Fire when footerbuttonclick
25090 * @param {Roo.bootstrap.UploadCropbox} this
25091 * @param {String} type
25093 "footerbuttonclick" : true,
25097 * @param {Roo.bootstrap.UploadCropbox} this
25102 * Fire when rotate the image
25103 * @param {Roo.bootstrap.UploadCropbox} this
25104 * @param {String} pos
25109 * Fire when inspect the file
25110 * @param {Roo.bootstrap.UploadCropbox} this
25111 * @param {Object} file
25116 * Fire when xhr upload the file
25117 * @param {Roo.bootstrap.UploadCropbox} this
25118 * @param {Object} data
25123 * Fire when arrange the file data
25124 * @param {Roo.bootstrap.UploadCropbox} this
25125 * @param {Object} formData
25130 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25133 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25135 emptyText : 'Click to upload image',
25136 rotateNotify : 'Image is too small to rotate',
25137 errorTimeout : 3000,
25151 cropType : 'image/jpeg',
25153 canvasLoaded : false,
25154 isDocument : false,
25156 paramName : 'imageUpload',
25158 loadingText : 'Loading...',
25161 getAutoCreate : function()
25165 cls : 'roo-upload-cropbox',
25169 cls : 'roo-upload-cropbox-selector',
25174 cls : 'roo-upload-cropbox-body',
25175 style : 'cursor:pointer',
25179 cls : 'roo-upload-cropbox-preview'
25183 cls : 'roo-upload-cropbox-thumb'
25187 cls : 'roo-upload-cropbox-empty-notify',
25188 html : this.emptyText
25192 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25193 html : this.rotateNotify
25199 cls : 'roo-upload-cropbox-footer',
25202 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25212 onRender : function(ct, position)
25214 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25216 if (this.buttons.length) {
25218 Roo.each(this.buttons, function(bb) {
25220 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25222 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25228 this.maskEl = this.el;
25232 initEvents : function()
25234 this.urlAPI = (window.createObjectURL && window) ||
25235 (window.URL && URL.revokeObjectURL && URL) ||
25236 (window.webkitURL && webkitURL);
25238 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25239 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25241 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25242 this.selectorEl.hide();
25244 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25245 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25247 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25248 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25249 this.thumbEl.hide();
25251 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25252 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25254 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25255 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25256 this.errorEl.hide();
25258 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25259 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25260 this.footerEl.hide();
25262 this.setThumbBoxSize();
25268 this.fireEvent('initial', this);
25275 window.addEventListener("resize", function() { _this.resize(); } );
25277 this.bodyEl.on('click', this.beforeSelectFile, this);
25280 this.bodyEl.on('touchstart', this.onTouchStart, this);
25281 this.bodyEl.on('touchmove', this.onTouchMove, this);
25282 this.bodyEl.on('touchend', this.onTouchEnd, this);
25286 this.bodyEl.on('mousedown', this.onMouseDown, this);
25287 this.bodyEl.on('mousemove', this.onMouseMove, this);
25288 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25289 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25290 Roo.get(document).on('mouseup', this.onMouseUp, this);
25293 this.selectorEl.on('change', this.onFileSelected, this);
25299 this.baseScale = 1;
25301 this.baseRotate = 1;
25302 this.dragable = false;
25303 this.pinching = false;
25306 this.cropData = false;
25307 this.notifyEl.dom.innerHTML = this.emptyText;
25309 this.selectorEl.dom.value = '';
25313 resize : function()
25315 if(this.fireEvent('resize', this) != false){
25316 this.setThumbBoxPosition();
25317 this.setCanvasPosition();
25321 onFooterButtonClick : function(e, el, o, type)
25324 case 'rotate-left' :
25325 this.onRotateLeft(e);
25327 case 'rotate-right' :
25328 this.onRotateRight(e);
25331 this.beforeSelectFile(e);
25346 this.fireEvent('footerbuttonclick', this, type);
25349 beforeSelectFile : function(e)
25351 e.preventDefault();
25353 if(this.fireEvent('beforeselectfile', this) != false){
25354 this.selectorEl.dom.click();
25358 onFileSelected : function(e)
25360 e.preventDefault();
25362 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25366 var file = this.selectorEl.dom.files[0];
25368 if(this.fireEvent('inspect', this, file) != false){
25369 this.prepare(file);
25374 trash : function(e)
25376 this.fireEvent('trash', this);
25379 download : function(e)
25381 this.fireEvent('download', this);
25384 loadCanvas : function(src)
25386 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25390 this.imageEl = document.createElement('img');
25394 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25396 this.imageEl.src = src;
25400 onLoadCanvas : function()
25402 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25403 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25405 this.bodyEl.un('click', this.beforeSelectFile, this);
25407 this.notifyEl.hide();
25408 this.thumbEl.show();
25409 this.footerEl.show();
25411 this.baseRotateLevel();
25413 if(this.isDocument){
25414 this.setThumbBoxSize();
25417 this.setThumbBoxPosition();
25419 this.baseScaleLevel();
25425 this.canvasLoaded = true;
25428 this.maskEl.unmask();
25433 setCanvasPosition : function()
25435 if(!this.canvasEl){
25439 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25440 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25442 this.previewEl.setLeft(pw);
25443 this.previewEl.setTop(ph);
25447 onMouseDown : function(e)
25451 this.dragable = true;
25452 this.pinching = false;
25454 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25455 this.dragable = false;
25459 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25460 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25464 onMouseMove : function(e)
25468 if(!this.canvasLoaded){
25472 if (!this.dragable){
25476 var minX = Math.ceil(this.thumbEl.getLeft(true));
25477 var minY = Math.ceil(this.thumbEl.getTop(true));
25479 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25480 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25482 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25483 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25485 x = x - this.mouseX;
25486 y = y - this.mouseY;
25488 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25489 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25491 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25492 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25494 this.previewEl.setLeft(bgX);
25495 this.previewEl.setTop(bgY);
25497 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25498 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25501 onMouseUp : function(e)
25505 this.dragable = false;
25508 onMouseWheel : function(e)
25512 this.startScale = this.scale;
25514 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25516 if(!this.zoomable()){
25517 this.scale = this.startScale;
25526 zoomable : function()
25528 var minScale = this.thumbEl.getWidth() / this.minWidth;
25530 if(this.minWidth < this.minHeight){
25531 minScale = this.thumbEl.getHeight() / this.minHeight;
25534 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25535 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25539 (this.rotate == 0 || this.rotate == 180) &&
25541 width > this.imageEl.OriginWidth ||
25542 height > this.imageEl.OriginHeight ||
25543 (width < this.minWidth && height < this.minHeight)
25551 (this.rotate == 90 || this.rotate == 270) &&
25553 width > this.imageEl.OriginWidth ||
25554 height > this.imageEl.OriginHeight ||
25555 (width < this.minHeight && height < this.minWidth)
25562 !this.isDocument &&
25563 (this.rotate == 0 || this.rotate == 180) &&
25565 width < this.minWidth ||
25566 width > this.imageEl.OriginWidth ||
25567 height < this.minHeight ||
25568 height > this.imageEl.OriginHeight
25575 !this.isDocument &&
25576 (this.rotate == 90 || this.rotate == 270) &&
25578 width < this.minHeight ||
25579 width > this.imageEl.OriginWidth ||
25580 height < this.minWidth ||
25581 height > this.imageEl.OriginHeight
25591 onRotateLeft : function(e)
25593 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25595 var minScale = this.thumbEl.getWidth() / this.minWidth;
25597 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25598 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25600 this.startScale = this.scale;
25602 while (this.getScaleLevel() < minScale){
25604 this.scale = this.scale + 1;
25606 if(!this.zoomable()){
25611 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25612 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25617 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25624 this.scale = this.startScale;
25626 this.onRotateFail();
25631 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25633 if(this.isDocument){
25634 this.setThumbBoxSize();
25635 this.setThumbBoxPosition();
25636 this.setCanvasPosition();
25641 this.fireEvent('rotate', this, 'left');
25645 onRotateRight : function(e)
25647 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25649 var minScale = this.thumbEl.getWidth() / this.minWidth;
25651 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25652 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25654 this.startScale = this.scale;
25656 while (this.getScaleLevel() < minScale){
25658 this.scale = this.scale + 1;
25660 if(!this.zoomable()){
25665 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25666 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25671 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25678 this.scale = this.startScale;
25680 this.onRotateFail();
25685 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25687 if(this.isDocument){
25688 this.setThumbBoxSize();
25689 this.setThumbBoxPosition();
25690 this.setCanvasPosition();
25695 this.fireEvent('rotate', this, 'right');
25698 onRotateFail : function()
25700 this.errorEl.show(true);
25704 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25709 this.previewEl.dom.innerHTML = '';
25711 var canvasEl = document.createElement("canvas");
25713 var contextEl = canvasEl.getContext("2d");
25715 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25716 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25717 var center = this.imageEl.OriginWidth / 2;
25719 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25720 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25721 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25722 center = this.imageEl.OriginHeight / 2;
25725 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25727 contextEl.translate(center, center);
25728 contextEl.rotate(this.rotate * Math.PI / 180);
25730 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25732 this.canvasEl = document.createElement("canvas");
25734 this.contextEl = this.canvasEl.getContext("2d");
25736 switch (this.rotate) {
25739 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25740 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25742 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25747 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25748 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25750 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25751 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);
25755 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25760 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25761 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25763 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25764 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);
25768 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);
25773 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25774 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25776 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25777 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25781 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);
25788 this.previewEl.appendChild(this.canvasEl);
25790 this.setCanvasPosition();
25795 if(!this.canvasLoaded){
25799 var imageCanvas = document.createElement("canvas");
25801 var imageContext = imageCanvas.getContext("2d");
25803 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25804 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25806 var center = imageCanvas.width / 2;
25808 imageContext.translate(center, center);
25810 imageContext.rotate(this.rotate * Math.PI / 180);
25812 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25814 var canvas = document.createElement("canvas");
25816 var context = canvas.getContext("2d");
25818 canvas.width = this.minWidth;
25819 canvas.height = this.minHeight;
25821 switch (this.rotate) {
25824 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25825 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25827 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25828 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25830 var targetWidth = this.minWidth - 2 * x;
25831 var targetHeight = this.minHeight - 2 * y;
25835 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25836 scale = targetWidth / width;
25839 if(x > 0 && y == 0){
25840 scale = targetHeight / height;
25843 if(x > 0 && y > 0){
25844 scale = targetWidth / width;
25846 if(width < height){
25847 scale = targetHeight / height;
25851 context.scale(scale, scale);
25853 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25854 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25856 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25857 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25859 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25864 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25865 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25867 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25868 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25870 var targetWidth = this.minWidth - 2 * x;
25871 var targetHeight = this.minHeight - 2 * y;
25875 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25876 scale = targetWidth / width;
25879 if(x > 0 && y == 0){
25880 scale = targetHeight / height;
25883 if(x > 0 && y > 0){
25884 scale = targetWidth / width;
25886 if(width < height){
25887 scale = targetHeight / height;
25891 context.scale(scale, scale);
25893 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25894 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25896 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25897 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25899 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25901 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25906 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25907 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25909 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25910 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25912 var targetWidth = this.minWidth - 2 * x;
25913 var targetHeight = this.minHeight - 2 * y;
25917 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25918 scale = targetWidth / width;
25921 if(x > 0 && y == 0){
25922 scale = targetHeight / height;
25925 if(x > 0 && y > 0){
25926 scale = targetWidth / width;
25928 if(width < height){
25929 scale = targetHeight / height;
25933 context.scale(scale, scale);
25935 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25936 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25938 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25939 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25941 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25942 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25944 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25949 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25950 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25952 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25953 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25955 var targetWidth = this.minWidth - 2 * x;
25956 var targetHeight = this.minHeight - 2 * y;
25960 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25961 scale = targetWidth / width;
25964 if(x > 0 && y == 0){
25965 scale = targetHeight / height;
25968 if(x > 0 && y > 0){
25969 scale = targetWidth / width;
25971 if(width < height){
25972 scale = targetHeight / height;
25976 context.scale(scale, scale);
25978 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25979 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25981 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25982 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25984 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25986 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25993 this.cropData = canvas.toDataURL(this.cropType);
25995 if(this.fireEvent('crop', this, this.cropData) !== false){
25996 this.process(this.file, this.cropData);
26003 setThumbBoxSize : function()
26007 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26008 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26009 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26011 this.minWidth = width;
26012 this.minHeight = height;
26014 if(this.rotate == 90 || this.rotate == 270){
26015 this.minWidth = height;
26016 this.minHeight = width;
26021 width = Math.ceil(this.minWidth * height / this.minHeight);
26023 if(this.minWidth > this.minHeight){
26025 height = Math.ceil(this.minHeight * width / this.minWidth);
26028 this.thumbEl.setStyle({
26029 width : width + 'px',
26030 height : height + 'px'
26037 setThumbBoxPosition : function()
26039 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26040 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26042 this.thumbEl.setLeft(x);
26043 this.thumbEl.setTop(y);
26047 baseRotateLevel : function()
26049 this.baseRotate = 1;
26052 typeof(this.exif) != 'undefined' &&
26053 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26054 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26056 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26059 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26063 baseScaleLevel : function()
26067 if(this.isDocument){
26069 if(this.baseRotate == 6 || this.baseRotate == 8){
26071 height = this.thumbEl.getHeight();
26072 this.baseScale = height / this.imageEl.OriginWidth;
26074 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26075 width = this.thumbEl.getWidth();
26076 this.baseScale = width / this.imageEl.OriginHeight;
26082 height = this.thumbEl.getHeight();
26083 this.baseScale = height / this.imageEl.OriginHeight;
26085 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26086 width = this.thumbEl.getWidth();
26087 this.baseScale = width / this.imageEl.OriginWidth;
26093 if(this.baseRotate == 6 || this.baseRotate == 8){
26095 width = this.thumbEl.getHeight();
26096 this.baseScale = width / this.imageEl.OriginHeight;
26098 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26099 height = this.thumbEl.getWidth();
26100 this.baseScale = height / this.imageEl.OriginHeight;
26103 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26104 height = this.thumbEl.getWidth();
26105 this.baseScale = height / this.imageEl.OriginHeight;
26107 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26108 width = this.thumbEl.getHeight();
26109 this.baseScale = width / this.imageEl.OriginWidth;
26116 width = this.thumbEl.getWidth();
26117 this.baseScale = width / this.imageEl.OriginWidth;
26119 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26120 height = this.thumbEl.getHeight();
26121 this.baseScale = height / this.imageEl.OriginHeight;
26124 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26126 height = this.thumbEl.getHeight();
26127 this.baseScale = height / this.imageEl.OriginHeight;
26129 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26130 width = this.thumbEl.getWidth();
26131 this.baseScale = width / this.imageEl.OriginWidth;
26139 getScaleLevel : function()
26141 return this.baseScale * Math.pow(1.1, this.scale);
26144 onTouchStart : function(e)
26146 if(!this.canvasLoaded){
26147 this.beforeSelectFile(e);
26151 var touches = e.browserEvent.touches;
26157 if(touches.length == 1){
26158 this.onMouseDown(e);
26162 if(touches.length != 2){
26168 for(var i = 0, finger; finger = touches[i]; i++){
26169 coords.push(finger.pageX, finger.pageY);
26172 var x = Math.pow(coords[0] - coords[2], 2);
26173 var y = Math.pow(coords[1] - coords[3], 2);
26175 this.startDistance = Math.sqrt(x + y);
26177 this.startScale = this.scale;
26179 this.pinching = true;
26180 this.dragable = false;
26184 onTouchMove : function(e)
26186 if(!this.pinching && !this.dragable){
26190 var touches = e.browserEvent.touches;
26197 this.onMouseMove(e);
26203 for(var i = 0, finger; finger = touches[i]; i++){
26204 coords.push(finger.pageX, finger.pageY);
26207 var x = Math.pow(coords[0] - coords[2], 2);
26208 var y = Math.pow(coords[1] - coords[3], 2);
26210 this.endDistance = Math.sqrt(x + y);
26212 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26214 if(!this.zoomable()){
26215 this.scale = this.startScale;
26223 onTouchEnd : function(e)
26225 this.pinching = false;
26226 this.dragable = false;
26230 process : function(file, crop)
26233 this.maskEl.mask(this.loadingText);
26236 this.xhr = new XMLHttpRequest();
26238 file.xhr = this.xhr;
26240 this.xhr.open(this.method, this.url, true);
26243 "Accept": "application/json",
26244 "Cache-Control": "no-cache",
26245 "X-Requested-With": "XMLHttpRequest"
26248 for (var headerName in headers) {
26249 var headerValue = headers[headerName];
26251 this.xhr.setRequestHeader(headerName, headerValue);
26257 this.xhr.onload = function()
26259 _this.xhrOnLoad(_this.xhr);
26262 this.xhr.onerror = function()
26264 _this.xhrOnError(_this.xhr);
26267 var formData = new FormData();
26269 formData.append('returnHTML', 'NO');
26272 formData.append('crop', crop);
26275 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26276 formData.append(this.paramName, file, file.name);
26279 if(typeof(file.filename) != 'undefined'){
26280 formData.append('filename', file.filename);
26283 if(typeof(file.mimetype) != 'undefined'){
26284 formData.append('mimetype', file.mimetype);
26287 if(this.fireEvent('arrange', this, formData) != false){
26288 this.xhr.send(formData);
26292 xhrOnLoad : function(xhr)
26295 this.maskEl.unmask();
26298 if (xhr.readyState !== 4) {
26299 this.fireEvent('exception', this, xhr);
26303 var response = Roo.decode(xhr.responseText);
26305 if(!response.success){
26306 this.fireEvent('exception', this, xhr);
26310 var response = Roo.decode(xhr.responseText);
26312 this.fireEvent('upload', this, response);
26316 xhrOnError : function()
26319 this.maskEl.unmask();
26322 Roo.log('xhr on error');
26324 var response = Roo.decode(xhr.responseText);
26330 prepare : function(file)
26333 this.maskEl.mask(this.loadingText);
26339 if(typeof(file) === 'string'){
26340 this.loadCanvas(file);
26344 if(!file || !this.urlAPI){
26349 this.cropType = file.type;
26353 if(this.fireEvent('prepare', this, this.file) != false){
26355 var reader = new FileReader();
26357 reader.onload = function (e) {
26358 if (e.target.error) {
26359 Roo.log(e.target.error);
26363 var buffer = e.target.result,
26364 dataView = new DataView(buffer),
26366 maxOffset = dataView.byteLength - 4,
26370 if (dataView.getUint16(0) === 0xffd8) {
26371 while (offset < maxOffset) {
26372 markerBytes = dataView.getUint16(offset);
26374 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26375 markerLength = dataView.getUint16(offset + 2) + 2;
26376 if (offset + markerLength > dataView.byteLength) {
26377 Roo.log('Invalid meta data: Invalid segment size.');
26381 if(markerBytes == 0xffe1){
26382 _this.parseExifData(
26389 offset += markerLength;
26399 var url = _this.urlAPI.createObjectURL(_this.file);
26401 _this.loadCanvas(url);
26406 reader.readAsArrayBuffer(this.file);
26412 parseExifData : function(dataView, offset, length)
26414 var tiffOffset = offset + 10,
26418 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26419 // No Exif data, might be XMP data instead
26423 // Check for the ASCII code for "Exif" (0x45786966):
26424 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26425 // No Exif data, might be XMP data instead
26428 if (tiffOffset + 8 > dataView.byteLength) {
26429 Roo.log('Invalid Exif data: Invalid segment size.');
26432 // Check for the two null bytes:
26433 if (dataView.getUint16(offset + 8) !== 0x0000) {
26434 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26437 // Check the byte alignment:
26438 switch (dataView.getUint16(tiffOffset)) {
26440 littleEndian = true;
26443 littleEndian = false;
26446 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26449 // Check for the TIFF tag marker (0x002A):
26450 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26451 Roo.log('Invalid Exif data: Missing TIFF marker.');
26454 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26455 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26457 this.parseExifTags(
26460 tiffOffset + dirOffset,
26465 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26470 if (dirOffset + 6 > dataView.byteLength) {
26471 Roo.log('Invalid Exif data: Invalid directory offset.');
26474 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26475 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26476 if (dirEndOffset + 4 > dataView.byteLength) {
26477 Roo.log('Invalid Exif data: Invalid directory size.');
26480 for (i = 0; i < tagsNumber; i += 1) {
26484 dirOffset + 2 + 12 * i, // tag offset
26488 // Return the offset to the next directory:
26489 return dataView.getUint32(dirEndOffset, littleEndian);
26492 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26494 var tag = dataView.getUint16(offset, littleEndian);
26496 this.exif[tag] = this.getExifValue(
26500 dataView.getUint16(offset + 2, littleEndian), // tag type
26501 dataView.getUint32(offset + 4, littleEndian), // tag length
26506 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26508 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26517 Roo.log('Invalid Exif data: Invalid tag type.');
26521 tagSize = tagType.size * length;
26522 // Determine if the value is contained in the dataOffset bytes,
26523 // or if the value at the dataOffset is a pointer to the actual data:
26524 dataOffset = tagSize > 4 ?
26525 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26526 if (dataOffset + tagSize > dataView.byteLength) {
26527 Roo.log('Invalid Exif data: Invalid data offset.');
26530 if (length === 1) {
26531 return tagType.getValue(dataView, dataOffset, littleEndian);
26534 for (i = 0; i < length; i += 1) {
26535 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26538 if (tagType.ascii) {
26540 // Concatenate the chars:
26541 for (i = 0; i < values.length; i += 1) {
26543 // Ignore the terminating NULL byte(s):
26544 if (c === '\u0000') {
26556 Roo.apply(Roo.bootstrap.UploadCropbox, {
26558 'Orientation': 0x0112
26562 1: 0, //'top-left',
26564 3: 180, //'bottom-right',
26565 // 4: 'bottom-left',
26567 6: 90, //'right-top',
26568 // 7: 'right-bottom',
26569 8: 270 //'left-bottom'
26573 // byte, 8-bit unsigned int:
26575 getValue: function (dataView, dataOffset) {
26576 return dataView.getUint8(dataOffset);
26580 // ascii, 8-bit byte:
26582 getValue: function (dataView, dataOffset) {
26583 return String.fromCharCode(dataView.getUint8(dataOffset));
26588 // short, 16 bit int:
26590 getValue: function (dataView, dataOffset, littleEndian) {
26591 return dataView.getUint16(dataOffset, littleEndian);
26595 // long, 32 bit int:
26597 getValue: function (dataView, dataOffset, littleEndian) {
26598 return dataView.getUint32(dataOffset, littleEndian);
26602 // rational = two long values, first is numerator, second is denominator:
26604 getValue: function (dataView, dataOffset, littleEndian) {
26605 return dataView.getUint32(dataOffset, littleEndian) /
26606 dataView.getUint32(dataOffset + 4, littleEndian);
26610 // slong, 32 bit signed int:
26612 getValue: function (dataView, dataOffset, littleEndian) {
26613 return dataView.getInt32(dataOffset, littleEndian);
26617 // srational, two slongs, first is numerator, second is denominator:
26619 getValue: function (dataView, dataOffset, littleEndian) {
26620 return dataView.getInt32(dataOffset, littleEndian) /
26621 dataView.getInt32(dataOffset + 4, littleEndian);
26631 cls : 'btn-group roo-upload-cropbox-rotate-left',
26632 action : 'rotate-left',
26636 cls : 'btn btn-default',
26637 html : '<i class="fa fa-undo"></i>'
26643 cls : 'btn-group roo-upload-cropbox-picture',
26644 action : 'picture',
26648 cls : 'btn btn-default',
26649 html : '<i class="fa fa-picture-o"></i>'
26655 cls : 'btn-group roo-upload-cropbox-rotate-right',
26656 action : 'rotate-right',
26660 cls : 'btn btn-default',
26661 html : '<i class="fa fa-repeat"></i>'
26669 cls : 'btn-group roo-upload-cropbox-rotate-left',
26670 action : 'rotate-left',
26674 cls : 'btn btn-default',
26675 html : '<i class="fa fa-undo"></i>'
26681 cls : 'btn-group roo-upload-cropbox-download',
26682 action : 'download',
26686 cls : 'btn btn-default',
26687 html : '<i class="fa fa-download"></i>'
26693 cls : 'btn-group roo-upload-cropbox-crop',
26698 cls : 'btn btn-default',
26699 html : '<i class="fa fa-crop"></i>'
26705 cls : 'btn-group roo-upload-cropbox-trash',
26710 cls : 'btn btn-default',
26711 html : '<i class="fa fa-trash"></i>'
26717 cls : 'btn-group roo-upload-cropbox-rotate-right',
26718 action : 'rotate-right',
26722 cls : 'btn btn-default',
26723 html : '<i class="fa fa-repeat"></i>'
26731 cls : 'btn-group roo-upload-cropbox-rotate-left',
26732 action : 'rotate-left',
26736 cls : 'btn btn-default',
26737 html : '<i class="fa fa-undo"></i>'
26743 cls : 'btn-group roo-upload-cropbox-rotate-right',
26744 action : 'rotate-right',
26748 cls : 'btn btn-default',
26749 html : '<i class="fa fa-repeat"></i>'
26762 * @class Roo.bootstrap.DocumentManager
26763 * @extends Roo.bootstrap.Component
26764 * Bootstrap DocumentManager class
26765 * @cfg {String} paramName default 'imageUpload'
26766 * @cfg {String} method default POST
26767 * @cfg {String} url action url
26768 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26769 * @cfg {Boolean} multiple multiple upload default true
26770 * @cfg {Number} thumbSize default 300
26771 * @cfg {String} fieldLabel
26772 * @cfg {Number} labelWidth default 4
26773 * @cfg {String} labelAlign (left|top) default left
26774 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26777 * Create a new DocumentManager
26778 * @param {Object} config The config object
26781 Roo.bootstrap.DocumentManager = function(config){
26782 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26787 * Fire when initial the DocumentManager
26788 * @param {Roo.bootstrap.DocumentManager} this
26793 * inspect selected file
26794 * @param {Roo.bootstrap.DocumentManager} this
26795 * @param {File} file
26800 * Fire when xhr load exception
26801 * @param {Roo.bootstrap.DocumentManager} this
26802 * @param {XMLHttpRequest} xhr
26804 "exception" : true,
26807 * prepare the form data
26808 * @param {Roo.bootstrap.DocumentManager} this
26809 * @param {Object} formData
26814 * Fire when remove the file
26815 * @param {Roo.bootstrap.DocumentManager} this
26816 * @param {Object} file
26821 * Fire after refresh the file
26822 * @param {Roo.bootstrap.DocumentManager} this
26827 * Fire after click the image
26828 * @param {Roo.bootstrap.DocumentManager} this
26829 * @param {Object} file
26834 * Fire when upload a image and editable set to true
26835 * @param {Roo.bootstrap.DocumentManager} this
26836 * @param {Object} file
26840 * @event beforeselectfile
26841 * Fire before select file
26842 * @param {Roo.bootstrap.DocumentManager} this
26844 "beforeselectfile" : true,
26847 * Fire before process file
26848 * @param {Roo.bootstrap.DocumentManager} this
26849 * @param {Object} file
26856 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26865 paramName : 'imageUpload',
26868 labelAlign : 'left',
26875 getAutoCreate : function()
26877 var managerWidget = {
26879 cls : 'roo-document-manager',
26883 cls : 'roo-document-manager-selector',
26888 cls : 'roo-document-manager-uploader',
26892 cls : 'roo-document-manager-upload-btn',
26893 html : '<i class="fa fa-plus"></i>'
26904 cls : 'column col-md-12',
26909 if(this.fieldLabel.length){
26914 cls : 'column col-md-12',
26915 html : this.fieldLabel
26919 cls : 'column col-md-12',
26924 if(this.labelAlign == 'left'){
26928 cls : 'column col-md-' + this.labelWidth,
26929 html : this.fieldLabel
26933 cls : 'column col-md-' + (12 - this.labelWidth),
26943 cls : 'row clearfix',
26951 initEvents : function()
26953 this.managerEl = this.el.select('.roo-document-manager', true).first();
26954 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26956 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26957 this.selectorEl.hide();
26960 this.selectorEl.attr('multiple', 'multiple');
26963 this.selectorEl.on('change', this.onFileSelected, this);
26965 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26966 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26968 this.uploader.on('click', this.onUploaderClick, this);
26970 this.renderProgressDialog();
26974 window.addEventListener("resize", function() { _this.refresh(); } );
26976 this.fireEvent('initial', this);
26979 renderProgressDialog : function()
26983 this.progressDialog = new Roo.bootstrap.Modal({
26984 cls : 'roo-document-manager-progress-dialog',
26985 allow_close : false,
26995 btnclick : function() {
26996 _this.uploadCancel();
27002 this.progressDialog.render(Roo.get(document.body));
27004 this.progress = new Roo.bootstrap.Progress({
27005 cls : 'roo-document-manager-progress',
27010 this.progress.render(this.progressDialog.getChildContainer());
27012 this.progressBar = new Roo.bootstrap.ProgressBar({
27013 cls : 'roo-document-manager-progress-bar',
27016 aria_valuemax : 12,
27020 this.progressBar.render(this.progress.getChildContainer());
27023 onUploaderClick : function(e)
27025 e.preventDefault();
27027 if(this.fireEvent('beforeselectfile', this) != false){
27028 this.selectorEl.dom.click();
27033 onFileSelected : function(e)
27035 e.preventDefault();
27037 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27041 Roo.each(this.selectorEl.dom.files, function(file){
27042 if(this.fireEvent('inspect', this, file) != false){
27043 this.files.push(file);
27053 this.selectorEl.dom.value = '';
27055 if(!this.files.length){
27059 if(this.boxes > 0 && this.files.length > this.boxes){
27060 this.files = this.files.slice(0, this.boxes);
27063 this.uploader.show();
27065 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27066 this.uploader.hide();
27075 Roo.each(this.files, function(file){
27077 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27078 var f = this.renderPreview(file);
27083 if(file.type.indexOf('image') != -1){
27084 this.delegates.push(
27086 _this.process(file);
27087 }).createDelegate(this)
27095 _this.process(file);
27096 }).createDelegate(this)
27101 this.files = files;
27103 this.delegates = this.delegates.concat(docs);
27105 if(!this.delegates.length){
27110 this.progressBar.aria_valuemax = this.delegates.length;
27117 arrange : function()
27119 if(!this.delegates.length){
27120 this.progressDialog.hide();
27125 var delegate = this.delegates.shift();
27127 this.progressDialog.show();
27129 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27131 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27136 refresh : function()
27138 this.uploader.show();
27140 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27141 this.uploader.hide();
27144 Roo.isTouch ? this.closable(false) : this.closable(true);
27146 this.fireEvent('refresh', this);
27149 onRemove : function(e, el, o)
27151 e.preventDefault();
27153 this.fireEvent('remove', this, o);
27157 remove : function(o)
27161 Roo.each(this.files, function(file){
27162 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27171 this.files = files;
27178 Roo.each(this.files, function(file){
27183 file.target.remove();
27192 onClick : function(e, el, o)
27194 e.preventDefault();
27196 this.fireEvent('click', this, o);
27200 closable : function(closable)
27202 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27204 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27216 xhrOnLoad : function(xhr)
27218 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27222 if (xhr.readyState !== 4) {
27224 this.fireEvent('exception', this, xhr);
27228 var response = Roo.decode(xhr.responseText);
27230 if(!response.success){
27232 this.fireEvent('exception', this, xhr);
27236 var file = this.renderPreview(response.data);
27238 this.files.push(file);
27244 xhrOnError : function(xhr)
27246 Roo.log('xhr on error');
27248 var response = Roo.decode(xhr.responseText);
27255 process : function(file)
27257 if(this.fireEvent('process', this, file) !== false){
27258 if(this.editable && file.type.indexOf('image') != -1){
27259 this.fireEvent('edit', this, file);
27263 this.uploadStart(file, false);
27270 uploadStart : function(file, crop)
27272 this.xhr = new XMLHttpRequest();
27274 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27279 file.xhr = this.xhr;
27281 this.managerEl.createChild({
27283 cls : 'roo-document-manager-loading',
27287 tooltip : file.name,
27288 cls : 'roo-document-manager-thumb',
27289 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27295 this.xhr.open(this.method, this.url, true);
27298 "Accept": "application/json",
27299 "Cache-Control": "no-cache",
27300 "X-Requested-With": "XMLHttpRequest"
27303 for (var headerName in headers) {
27304 var headerValue = headers[headerName];
27306 this.xhr.setRequestHeader(headerName, headerValue);
27312 this.xhr.onload = function()
27314 _this.xhrOnLoad(_this.xhr);
27317 this.xhr.onerror = function()
27319 _this.xhrOnError(_this.xhr);
27322 var formData = new FormData();
27324 formData.append('returnHTML', 'NO');
27327 formData.append('crop', crop);
27330 formData.append(this.paramName, file, file.name);
27332 if(this.fireEvent('prepare', this, formData) != false){
27333 this.xhr.send(formData);
27337 uploadCancel : function()
27344 this.delegates = [];
27346 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27353 renderPreview : function(file)
27355 if(typeof(file.target) != 'undefined' && file.target){
27359 var previewEl = this.managerEl.createChild({
27361 cls : 'roo-document-manager-preview',
27365 tooltip : file.filename,
27366 cls : 'roo-document-manager-thumb',
27367 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27372 html : '<i class="fa fa-times-circle"></i>'
27377 var close = previewEl.select('button.close', true).first();
27379 close.on('click', this.onRemove, this, file);
27381 file.target = previewEl;
27383 var image = previewEl.select('img', true).first();
27387 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27389 image.on('click', this.onClick, this, file);
27395 onPreviewLoad : function(file, image)
27397 if(typeof(file.target) == 'undefined' || !file.target){
27401 var width = image.dom.naturalWidth || image.dom.width;
27402 var height = image.dom.naturalHeight || image.dom.height;
27404 if(width > height){
27405 file.target.addClass('wide');
27409 file.target.addClass('tall');
27414 uploadFromSource : function(file, crop)
27416 this.xhr = new XMLHttpRequest();
27418 this.managerEl.createChild({
27420 cls : 'roo-document-manager-loading',
27424 tooltip : file.name,
27425 cls : 'roo-document-manager-thumb',
27426 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27432 this.xhr.open(this.method, this.url, true);
27435 "Accept": "application/json",
27436 "Cache-Control": "no-cache",
27437 "X-Requested-With": "XMLHttpRequest"
27440 for (var headerName in headers) {
27441 var headerValue = headers[headerName];
27443 this.xhr.setRequestHeader(headerName, headerValue);
27449 this.xhr.onload = function()
27451 _this.xhrOnLoad(_this.xhr);
27454 this.xhr.onerror = function()
27456 _this.xhrOnError(_this.xhr);
27459 var formData = new FormData();
27461 formData.append('returnHTML', 'NO');
27463 formData.append('crop', crop);
27465 if(typeof(file.filename) != 'undefined'){
27466 formData.append('filename', file.filename);
27469 if(typeof(file.mimetype) != 'undefined'){
27470 formData.append('mimetype', file.mimetype);
27473 if(this.fireEvent('prepare', this, formData) != false){
27474 this.xhr.send(formData);
27484 * @class Roo.bootstrap.DocumentViewer
27485 * @extends Roo.bootstrap.Component
27486 * Bootstrap DocumentViewer class
27489 * Create a new DocumentViewer
27490 * @param {Object} config The config object
27493 Roo.bootstrap.DocumentViewer = function(config){
27494 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27499 * Fire after initEvent
27500 * @param {Roo.bootstrap.DocumentViewer} this
27506 * @param {Roo.bootstrap.DocumentViewer} this
27511 * Fire after trash button
27512 * @param {Roo.bootstrap.DocumentViewer} this
27519 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27521 getAutoCreate : function()
27525 cls : 'roo-document-viewer',
27529 cls : 'roo-document-viewer-body',
27533 cls : 'roo-document-viewer-thumb',
27537 cls : 'roo-document-viewer-image'
27545 cls : 'roo-document-viewer-footer',
27548 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27556 cls : 'btn btn-default roo-document-viewer-trash',
27557 html : '<i class="fa fa-trash"></i>'
27570 initEvents : function()
27573 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27574 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27576 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27577 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27579 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27580 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27582 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27583 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27585 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27586 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27588 this.bodyEl.on('click', this.onClick, this);
27590 this.trashBtn.on('click', this.onTrash, this);
27594 initial : function()
27596 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27599 this.fireEvent('initial', this);
27603 onClick : function(e)
27605 e.preventDefault();
27607 this.fireEvent('click', this);
27610 onTrash : function(e)
27612 e.preventDefault();
27614 this.fireEvent('trash', this);
27626 * @class Roo.bootstrap.NavProgressBar
27627 * @extends Roo.bootstrap.Component
27628 * Bootstrap NavProgressBar class
27631 * Create a new nav progress bar
27632 * @param {Object} config The config object
27635 Roo.bootstrap.NavProgressBar = function(config){
27636 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27638 this.bullets = this.bullets || [];
27640 // Roo.bootstrap.NavProgressBar.register(this);
27644 * Fires when the active item changes
27645 * @param {Roo.bootstrap.NavProgressBar} this
27646 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27647 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27654 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27659 getAutoCreate : function()
27661 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27665 cls : 'roo-navigation-bar-group',
27669 cls : 'roo-navigation-top-bar'
27673 cls : 'roo-navigation-bullets-bar',
27677 cls : 'roo-navigation-bar'
27684 cls : 'roo-navigation-bottom-bar'
27694 initEvents: function()
27699 onRender : function(ct, position)
27701 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27703 if(this.bullets.length){
27704 Roo.each(this.bullets, function(b){
27713 addItem : function(cfg)
27715 var item = new Roo.bootstrap.NavProgressItem(cfg);
27717 item.parentId = this.id;
27718 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27721 var top = new Roo.bootstrap.Element({
27723 cls : 'roo-navigation-bar-text'
27726 var bottom = new Roo.bootstrap.Element({
27728 cls : 'roo-navigation-bar-text'
27731 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27732 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27734 var topText = new Roo.bootstrap.Element({
27736 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27739 var bottomText = new Roo.bootstrap.Element({
27741 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27744 topText.onRender(top.el, null);
27745 bottomText.onRender(bottom.el, null);
27748 item.bottomEl = bottom;
27751 this.barItems.push(item);
27756 getActive : function()
27758 var active = false;
27760 Roo.each(this.barItems, function(v){
27762 if (!v.isActive()) {
27774 setActiveItem : function(item)
27778 Roo.each(this.barItems, function(v){
27779 if (v.rid == item.rid) {
27783 if (v.isActive()) {
27784 v.setActive(false);
27789 item.setActive(true);
27791 this.fireEvent('changed', this, item, prev);
27794 getBarItem: function(rid)
27798 Roo.each(this.barItems, function(e) {
27799 if (e.rid != rid) {
27810 indexOfItem : function(item)
27814 Roo.each(this.barItems, function(v, i){
27816 if (v.rid != item.rid) {
27827 setActiveNext : function()
27829 var i = this.indexOfItem(this.getActive());
27831 if (i > this.barItems.length) {
27835 this.setActiveItem(this.barItems[i+1]);
27838 setActivePrev : function()
27840 var i = this.indexOfItem(this.getActive());
27846 this.setActiveItem(this.barItems[i-1]);
27849 format : function()
27851 if(!this.barItems.length){
27855 var width = 100 / this.barItems.length;
27857 Roo.each(this.barItems, function(i){
27858 i.el.setStyle('width', width + '%');
27859 i.topEl.el.setStyle('width', width + '%');
27860 i.bottomEl.el.setStyle('width', width + '%');
27869 * Nav Progress Item
27874 * @class Roo.bootstrap.NavProgressItem
27875 * @extends Roo.bootstrap.Component
27876 * Bootstrap NavProgressItem class
27877 * @cfg {String} rid the reference id
27878 * @cfg {Boolean} active (true|false) Is item active default false
27879 * @cfg {Boolean} disabled (true|false) Is item active default false
27880 * @cfg {String} html
27881 * @cfg {String} position (top|bottom) text position default bottom
27882 * @cfg {String} icon show icon instead of number
27885 * Create a new NavProgressItem
27886 * @param {Object} config The config object
27888 Roo.bootstrap.NavProgressItem = function(config){
27889 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27894 * The raw click event for the entire grid.
27895 * @param {Roo.bootstrap.NavProgressItem} this
27896 * @param {Roo.EventObject} e
27903 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27909 position : 'bottom',
27912 getAutoCreate : function()
27914 var iconCls = 'roo-navigation-bar-item-icon';
27916 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27920 cls: 'roo-navigation-bar-item',
27930 cfg.cls += ' active';
27933 cfg.cls += ' disabled';
27939 disable : function()
27941 this.setDisabled(true);
27944 enable : function()
27946 this.setDisabled(false);
27949 initEvents: function()
27951 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27953 this.iconEl.on('click', this.onClick, this);
27956 onClick : function(e)
27958 e.preventDefault();
27964 if(this.fireEvent('click', this, e) === false){
27968 this.parent().setActiveItem(this);
27971 isActive: function ()
27973 return this.active;
27976 setActive : function(state)
27978 if(this.active == state){
27982 this.active = state;
27985 this.el.addClass('active');
27989 this.el.removeClass('active');
27994 setDisabled : function(state)
27996 if(this.disabled == state){
28000 this.disabled = state;
28003 this.el.addClass('disabled');
28007 this.el.removeClass('disabled');
28010 tooltipEl : function()
28012 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28025 * @class Roo.bootstrap.FieldLabel
28026 * @extends Roo.bootstrap.Component
28027 * Bootstrap FieldLabel class
28028 * @cfg {String} html contents of the element
28029 * @cfg {String} tag tag of the element default label
28030 * @cfg {String} cls class of the element
28031 * @cfg {String} target label target
28032 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28033 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28034 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28035 * @cfg {String} iconTooltip default "This field is required"
28038 * Create a new FieldLabel
28039 * @param {Object} config The config object
28042 Roo.bootstrap.FieldLabel = function(config){
28043 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28048 * Fires after the field has been marked as invalid.
28049 * @param {Roo.form.FieldLabel} this
28050 * @param {String} msg The validation message
28055 * Fires after the field has been validated with no errors.
28056 * @param {Roo.form.FieldLabel} this
28062 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28069 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28070 validClass : 'text-success fa fa-lg fa-check',
28071 iconTooltip : 'This field is required',
28073 getAutoCreate : function(){
28077 cls : 'roo-bootstrap-field-label ' + this.cls,
28083 tooltip : this.iconTooltip
28095 initEvents: function()
28097 Roo.bootstrap.Element.superclass.initEvents.call(this);
28099 this.iconEl = this.el.select('i', true).first();
28101 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28103 Roo.bootstrap.FieldLabel.register(this);
28107 * Mark this field as valid
28109 markValid : function()
28111 this.iconEl.show();
28113 this.iconEl.removeClass(this.invalidClass);
28115 this.iconEl.addClass(this.validClass);
28117 this.fireEvent('valid', this);
28121 * Mark this field as invalid
28122 * @param {String} msg The validation message
28124 markInvalid : function(msg)
28126 this.iconEl.show();
28128 this.iconEl.removeClass(this.validClass);
28130 this.iconEl.addClass(this.invalidClass);
28132 this.fireEvent('invalid', this, msg);
28138 Roo.apply(Roo.bootstrap.FieldLabel, {
28143 * register a FieldLabel Group
28144 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28146 register : function(label)
28148 if(this.groups.hasOwnProperty(label.target)){
28152 this.groups[label.target] = label;
28156 * fetch a FieldLabel Group based on the target
28157 * @param {string} target
28158 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28160 get: function(target) {
28161 if (typeof(this.groups[target]) == 'undefined') {
28165 return this.groups[target] ;
28174 * page DateSplitField.
28180 * @class Roo.bootstrap.DateSplitField
28181 * @extends Roo.bootstrap.Component
28182 * Bootstrap DateSplitField class
28183 * @cfg {string} fieldLabel - the label associated
28184 * @cfg {Number} labelWidth set the width of label (0-12)
28185 * @cfg {String} labelAlign (top|left)
28186 * @cfg {Boolean} dayAllowBlank (true|false) default false
28187 * @cfg {Boolean} monthAllowBlank (true|false) default false
28188 * @cfg {Boolean} yearAllowBlank (true|false) default false
28189 * @cfg {string} dayPlaceholder
28190 * @cfg {string} monthPlaceholder
28191 * @cfg {string} yearPlaceholder
28192 * @cfg {string} dayFormat default 'd'
28193 * @cfg {string} monthFormat default 'm'
28194 * @cfg {string} yearFormat default 'Y'
28198 * Create a new DateSplitField
28199 * @param {Object} config The config object
28202 Roo.bootstrap.DateSplitField = function(config){
28203 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28209 * getting the data of years
28210 * @param {Roo.bootstrap.DateSplitField} this
28211 * @param {Object} years
28216 * getting the data of days
28217 * @param {Roo.bootstrap.DateSplitField} this
28218 * @param {Object} days
28223 * Fires after the field has been marked as invalid.
28224 * @param {Roo.form.Field} this
28225 * @param {String} msg The validation message
28230 * Fires after the field has been validated with no errors.
28231 * @param {Roo.form.Field} this
28237 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28240 labelAlign : 'top',
28242 dayAllowBlank : false,
28243 monthAllowBlank : false,
28244 yearAllowBlank : false,
28245 dayPlaceholder : '',
28246 monthPlaceholder : '',
28247 yearPlaceholder : '',
28251 isFormField : true,
28253 getAutoCreate : function()
28257 cls : 'row roo-date-split-field-group',
28262 cls : 'form-hidden-field roo-date-split-field-group-value',
28268 if(this.fieldLabel){
28271 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28275 html : this.fieldLabel
28281 Roo.each(['day', 'month', 'year'], function(t){
28284 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28291 inputEl: function ()
28293 return this.el.select('.roo-date-split-field-group-value', true).first();
28296 onRender : function(ct, position)
28300 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28302 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28304 this.dayField = new Roo.bootstrap.ComboBox({
28305 allowBlank : this.dayAllowBlank,
28306 alwaysQuery : true,
28307 displayField : 'value',
28310 forceSelection : true,
28312 placeholder : this.dayPlaceholder,
28313 selectOnFocus : true,
28314 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28315 triggerAction : 'all',
28317 valueField : 'value',
28318 store : new Roo.data.SimpleStore({
28319 data : (function() {
28321 _this.fireEvent('days', _this, days);
28324 fields : [ 'value' ]
28327 select : function (_self, record, index)
28329 _this.setValue(_this.getValue());
28334 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28336 this.monthField = new Roo.bootstrap.MonthField({
28337 after : '<i class=\"fa fa-calendar\"></i>',
28338 allowBlank : this.monthAllowBlank,
28339 placeholder : this.monthPlaceholder,
28342 render : function (_self)
28344 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28345 e.preventDefault();
28349 select : function (_self, oldvalue, newvalue)
28351 _this.setValue(_this.getValue());
28356 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28358 this.yearField = new Roo.bootstrap.ComboBox({
28359 allowBlank : this.yearAllowBlank,
28360 alwaysQuery : true,
28361 displayField : 'value',
28364 forceSelection : true,
28366 placeholder : this.yearPlaceholder,
28367 selectOnFocus : true,
28368 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28369 triggerAction : 'all',
28371 valueField : 'value',
28372 store : new Roo.data.SimpleStore({
28373 data : (function() {
28375 _this.fireEvent('years', _this, years);
28378 fields : [ 'value' ]
28381 select : function (_self, record, index)
28383 _this.setValue(_this.getValue());
28388 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28391 setValue : function(v, format)
28393 this.inputEl.dom.value = v;
28395 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28397 var d = Date.parseDate(v, f);
28404 this.setDay(d.format(this.dayFormat));
28405 this.setMonth(d.format(this.monthFormat));
28406 this.setYear(d.format(this.yearFormat));
28413 setDay : function(v)
28415 this.dayField.setValue(v);
28416 this.inputEl.dom.value = this.getValue();
28421 setMonth : function(v)
28423 this.monthField.setValue(v, true);
28424 this.inputEl.dom.value = this.getValue();
28429 setYear : function(v)
28431 this.yearField.setValue(v);
28432 this.inputEl.dom.value = this.getValue();
28437 getDay : function()
28439 return this.dayField.getValue();
28442 getMonth : function()
28444 return this.monthField.getValue();
28447 getYear : function()
28449 return this.yearField.getValue();
28452 getValue : function()
28454 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28456 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28466 this.inputEl.dom.value = '';
28471 validate : function()
28473 var d = this.dayField.validate();
28474 var m = this.monthField.validate();
28475 var y = this.yearField.validate();
28480 (!this.dayAllowBlank && !d) ||
28481 (!this.monthAllowBlank && !m) ||
28482 (!this.yearAllowBlank && !y)
28487 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28496 this.markInvalid();
28501 markValid : function()
28504 var label = this.el.select('label', true).first();
28505 var icon = this.el.select('i.fa-star', true).first();
28511 this.fireEvent('valid', this);
28515 * Mark this field as invalid
28516 * @param {String} msg The validation message
28518 markInvalid : function(msg)
28521 var label = this.el.select('label', true).first();
28522 var icon = this.el.select('i.fa-star', true).first();
28524 if(label && !icon){
28525 this.el.select('.roo-date-split-field-label', true).createChild({
28527 cls : 'text-danger fa fa-lg fa-star',
28528 tooltip : 'This field is required',
28529 style : 'margin-right:5px;'
28533 this.fireEvent('invalid', this, msg);
28536 clearInvalid : function()
28538 var label = this.el.select('label', true).first();
28539 var icon = this.el.select('i.fa-star', true).first();
28545 this.fireEvent('valid', this);
28548 getName: function()
28558 * http://masonry.desandro.com
28560 * The idea is to render all the bricks based on vertical width...
28562 * The original code extends 'outlayer' - we might need to use that....
28568 * @class Roo.bootstrap.LayoutMasonry
28569 * @extends Roo.bootstrap.Component
28570 * Bootstrap Layout Masonry class
28573 * Create a new Element
28574 * @param {Object} config The config object
28577 Roo.bootstrap.LayoutMasonry = function(config){
28578 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28584 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
28587 * @cfg {Boolean} isLayoutInstant = no animation?
28589 isLayoutInstant : false, // needed?
28592 * @cfg {Number} boxWidth width of the columns
28597 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
28602 * @cfg {Number} padWidth padding below box..
28607 * @cfg {Number} gutter gutter width..
28612 * @cfg {Number} maxCols maximum number of columns
28618 * @cfg {Boolean} isAutoInitial defalut true
28620 isAutoInitial : true,
28625 * @cfg {Boolean} isHorizontal defalut false
28627 isHorizontal : false,
28629 currentSize : null,
28635 bricks: null, //CompositeElement
28639 _isLayoutInited : false,
28641 // isAlternative : false, // only use for vertical layout...
28644 * @cfg {Number} alternativePadWidth padding below box..
28646 alternativePadWidth : 50,
28648 getAutoCreate : function(){
28652 cls: 'blog-masonary-wrapper ' + this.cls,
28654 cls : 'mas-boxes masonary'
28661 getChildContainer: function( )
28663 if (this.boxesEl) {
28664 return this.boxesEl;
28667 this.boxesEl = this.el.select('.mas-boxes').first();
28669 return this.boxesEl;
28673 initEvents : function()
28677 if(this.isAutoInitial){
28678 Roo.log('hook children rendered');
28679 this.on('childrenrendered', function() {
28680 Roo.log('children rendered');
28686 initial : function()
28688 this.currentSize = this.el.getBox(true);
28690 Roo.EventManager.onWindowResize(this.resize, this);
28692 if(!this.isAutoInitial){
28700 //this.layout.defer(500,this);
28704 resize : function()
28708 var cs = this.el.getBox(true);
28710 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28711 Roo.log("no change in with or X");
28715 this.currentSize = cs;
28721 layout : function()
28723 this._resetLayout();
28725 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28727 this.layoutItems( isInstant );
28729 this._isLayoutInited = true;
28733 _resetLayout : function()
28735 if(this.isHorizontal){
28736 this.horizontalMeasureColumns();
28740 this.verticalMeasureColumns();
28744 verticalMeasureColumns : function()
28746 this.getContainerWidth();
28748 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28749 // this.colWidth = Math.floor(this.containerWidth * 0.8);
28753 var boxWidth = this.boxWidth + this.padWidth;
28755 if(this.containerWidth < this.boxWidth){
28756 boxWidth = this.containerWidth
28759 var containerWidth = this.containerWidth;
28761 var cols = Math.floor(containerWidth / boxWidth);
28763 this.cols = Math.max( cols, 1 );
28765 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28767 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28769 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28771 this.colWidth = boxWidth + avail - this.padWidth;
28773 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28774 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
28777 horizontalMeasureColumns : function()
28779 this.getContainerWidth();
28781 var boxWidth = this.boxWidth;
28783 if(this.containerWidth < boxWidth){
28784 boxWidth = this.containerWidth;
28787 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28789 this.el.setHeight(boxWidth);
28793 getContainerWidth : function()
28795 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
28798 layoutItems : function( isInstant )
28800 var items = Roo.apply([], this.bricks);
28802 if(this.isHorizontal){
28803 this._horizontalLayoutItems( items , isInstant );
28807 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28808 // this._verticalAlternativeLayoutItems( items , isInstant );
28812 this._verticalLayoutItems( items , isInstant );
28816 _verticalLayoutItems : function ( items , isInstant)
28818 if ( !items || !items.length ) {
28823 ['xs', 'xs', 'xs', 'tall'],
28824 ['xs', 'xs', 'tall'],
28825 ['xs', 'xs', 'sm'],
28826 ['xs', 'xs', 'xs'],
28832 ['sm', 'xs', 'xs'],
28836 ['tall', 'xs', 'xs', 'xs'],
28837 ['tall', 'xs', 'xs'],
28849 Roo.each(items, function(item, k){
28851 switch (item.size) {
28852 // these layouts take up a full box,
28863 boxes.push([item]);
28886 var filterPattern = function(box, length)
28894 var pattern = box.slice(0, length);
28898 Roo.each(pattern, function(i){
28899 format.push(i.size);
28902 Roo.each(standard, function(s){
28904 if(String(s) != String(format)){
28913 if(!match && length == 1){
28918 filterPattern(box, length - 1);
28922 queue.push(pattern);
28924 box = box.slice(length, box.length);
28926 filterPattern(box, 4);
28932 Roo.each(boxes, function(box, k){
28938 if(box.length == 1){
28943 filterPattern(box, 4);
28947 this._processVerticalLayoutQueue( queue, isInstant );
28951 // _verticalAlternativeLayoutItems : function( items , isInstant )
28953 // if ( !items || !items.length ) {
28957 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
28961 _horizontalLayoutItems : function ( items , isInstant)
28963 if ( !items || !items.length || items.length < 3) {
28969 var eItems = items.slice(0, 3);
28971 items = items.slice(3, items.length);
28974 ['xs', 'xs', 'xs', 'wide'],
28975 ['xs', 'xs', 'wide'],
28976 ['xs', 'xs', 'sm'],
28977 ['xs', 'xs', 'xs'],
28983 ['sm', 'xs', 'xs'],
28987 ['wide', 'xs', 'xs', 'xs'],
28988 ['wide', 'xs', 'xs'],
29001 Roo.each(items, function(item, k){
29003 switch (item.size) {
29014 boxes.push([item]);
29038 var filterPattern = function(box, length)
29046 var pattern = box.slice(0, length);
29050 Roo.each(pattern, function(i){
29051 format.push(i.size);
29054 Roo.each(standard, function(s){
29056 if(String(s) != String(format)){
29065 if(!match && length == 1){
29070 filterPattern(box, length - 1);
29074 queue.push(pattern);
29076 box = box.slice(length, box.length);
29078 filterPattern(box, 4);
29084 Roo.each(boxes, function(box, k){
29090 if(box.length == 1){
29095 filterPattern(box, 4);
29102 var pos = this.el.getBox(true);
29106 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29108 var hit_end = false;
29110 Roo.each(queue, function(box){
29114 Roo.each(box, function(b){
29116 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29126 Roo.each(box, function(b){
29128 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29131 mx = Math.max(mx, b.x);
29135 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29139 Roo.each(box, function(b){
29141 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29155 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29158 /** Sets position of item in DOM
29159 * @param {Element} item
29160 * @param {Number} x - horizontal position
29161 * @param {Number} y - vertical position
29162 * @param {Boolean} isInstant - disables transitions
29164 _processVerticalLayoutQueue : function( queue, isInstant )
29166 var pos = this.el.getBox(true);
29171 for (var i = 0; i < this.cols; i++){
29175 Roo.each(queue, function(box, k){
29177 var col = k % this.cols;
29179 Roo.each(box, function(b,kk){
29181 b.el.position('absolute');
29183 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29184 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29186 if(b.size == 'md-left' || b.size == 'md-right'){
29187 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29188 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29191 b.el.setWidth(width);
29192 b.el.setHeight(height);
29194 b.el.select('iframe',true).setSize(width,height);
29198 for (var i = 0; i < this.cols; i++){
29200 if(maxY[i] < maxY[col]){
29205 col = Math.min(col, i);
29209 x = pos.x + col * (this.colWidth + this.padWidth);
29213 var positions = [];
29215 switch (box.length){
29217 positions = this.getVerticalOneBoxColPositions(x, y, box);
29220 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29223 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29226 positions = this.getVerticalFourBoxColPositions(x, y, box);
29232 Roo.each(box, function(b,kk){
29234 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29236 var sz = b.el.getSize();
29238 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29246 for (var i = 0; i < this.cols; i++){
29247 mY = Math.max(mY, maxY[i]);
29250 this.el.setHeight(mY - pos.y);
29254 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29256 // var pos = this.el.getBox(true);
29259 // var maxX = pos.right;
29261 // var maxHeight = 0;
29263 // Roo.each(items, function(item, k){
29267 // item.el.position('absolute');
29269 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29271 // item.el.setWidth(width);
29273 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29275 // item.el.setHeight(height);
29278 // item.el.setXY([x, y], isInstant ? false : true);
29280 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29283 // y = y + height + this.alternativePadWidth;
29285 // maxHeight = maxHeight + height + this.alternativePadWidth;
29289 // this.el.setHeight(maxHeight);
29293 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29295 var pos = this.el.getBox(true);
29300 var maxX = pos.right;
29302 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29304 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29306 Roo.each(queue, function(box, k){
29308 Roo.each(box, function(b, kk){
29310 b.el.position('absolute');
29312 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29313 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29315 if(b.size == 'md-left' || b.size == 'md-right'){
29316 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29317 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29320 b.el.setWidth(width);
29321 b.el.setHeight(height);
29329 var positions = [];
29331 switch (box.length){
29333 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29336 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29339 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29342 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29348 Roo.each(box, function(b,kk){
29350 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29352 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29360 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29362 Roo.each(eItems, function(b,k){
29364 b.size = (k == 0) ? 'sm' : 'xs';
29365 b.x = (k == 0) ? 2 : 1;
29366 b.y = (k == 0) ? 2 : 1;
29368 b.el.position('absolute');
29370 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29372 b.el.setWidth(width);
29374 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29376 b.el.setHeight(height);
29380 var positions = [];
29383 x : maxX - this.unitWidth * 2 - this.gutter,
29388 x : maxX - this.unitWidth,
29389 y : minY + (this.unitWidth + this.gutter) * 2
29393 x : maxX - this.unitWidth * 3 - this.gutter * 2,
29397 Roo.each(eItems, function(b,k){
29399 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29405 getVerticalOneBoxColPositions : function(x, y, box)
29409 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29411 if(box[0].size == 'md-left'){
29415 if(box[0].size == 'md-right'){
29420 x : x + (this.unitWidth + this.gutter) * rand,
29427 getVerticalTwoBoxColPositions : function(x, y, box)
29431 if(box[0].size == 'xs'){
29435 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29439 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29453 x : x + (this.unitWidth + this.gutter) * 2,
29454 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29461 getVerticalThreeBoxColPositions : function(x, y, box)
29465 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29473 x : x + (this.unitWidth + this.gutter) * 1,
29478 x : x + (this.unitWidth + this.gutter) * 2,
29486 if(box[0].size == 'xs' && box[1].size == 'xs'){
29495 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29499 x : x + (this.unitWidth + this.gutter) * 1,
29513 x : x + (this.unitWidth + this.gutter) * 2,
29518 x : x + (this.unitWidth + this.gutter) * 2,
29519 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29526 getVerticalFourBoxColPositions : function(x, y, box)
29530 if(box[0].size == 'xs'){
29539 y : y + (this.unitHeight + this.gutter) * 1
29544 y : y + (this.unitHeight + this.gutter) * 2
29548 x : x + (this.unitWidth + this.gutter) * 1,
29562 x : x + (this.unitWidth + this.gutter) * 2,
29567 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29568 y : y + (this.unitHeight + this.gutter) * 1
29572 x : x + (this.unitWidth + this.gutter) * 2,
29573 y : y + (this.unitWidth + this.gutter) * 2
29580 getHorizontalOneBoxColPositions : function(maxX, minY, box)
29584 if(box[0].size == 'md-left'){
29586 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29593 if(box[0].size == 'md-right'){
29595 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29596 y : minY + (this.unitWidth + this.gutter) * 1
29602 var rand = Math.floor(Math.random() * (4 - box[0].y));
29605 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29606 y : minY + (this.unitWidth + this.gutter) * rand
29613 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29617 if(box[0].size == 'xs'){
29620 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29625 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29626 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29634 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29639 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29640 y : minY + (this.unitWidth + this.gutter) * 2
29647 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29651 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29654 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29659 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29660 y : minY + (this.unitWidth + this.gutter) * 1
29664 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29665 y : minY + (this.unitWidth + this.gutter) * 2
29672 if(box[0].size == 'xs' && box[1].size == 'xs'){
29675 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29680 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29685 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29686 y : minY + (this.unitWidth + this.gutter) * 1
29694 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29699 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29700 y : minY + (this.unitWidth + this.gutter) * 2
29704 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29705 y : minY + (this.unitWidth + this.gutter) * 2
29712 getHorizontalFourBoxColPositions : function(maxX, minY, box)
29716 if(box[0].size == 'xs'){
29719 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29724 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29729 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),
29734 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29735 y : minY + (this.unitWidth + this.gutter) * 1
29743 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29748 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29749 y : minY + (this.unitWidth + this.gutter) * 2
29753 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29754 y : minY + (this.unitWidth + this.gutter) * 2
29758 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),
29759 y : minY + (this.unitWidth + this.gutter) * 2
29773 * http://masonry.desandro.com
29775 * The idea is to render all the bricks based on vertical width...
29777 * The original code extends 'outlayer' - we might need to use that....
29783 * @class Roo.bootstrap.LayoutMasonryAuto
29784 * @extends Roo.bootstrap.Component
29785 * Bootstrap Layout Masonry class
29788 * Create a new Element
29789 * @param {Object} config The config object
29792 Roo.bootstrap.LayoutMasonryAuto = function(config){
29793 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29796 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
29799 * @cfg {Boolean} isFitWidth - resize the width..
29801 isFitWidth : false, // options..
29803 * @cfg {Boolean} isOriginLeft = left align?
29805 isOriginLeft : true,
29807 * @cfg {Boolean} isOriginTop = top align?
29809 isOriginTop : false,
29811 * @cfg {Boolean} isLayoutInstant = no animation?
29813 isLayoutInstant : false, // needed?
29815 * @cfg {Boolean} isResizingContainer = not sure if this is used..
29817 isResizingContainer : true,
29819 * @cfg {Number} columnWidth width of the columns
29825 * @cfg {Number} maxCols maximum number of columns
29830 * @cfg {Number} padHeight padding below box..
29836 * @cfg {Boolean} isAutoInitial defalut true
29839 isAutoInitial : true,
29845 initialColumnWidth : 0,
29846 currentSize : null,
29848 colYs : null, // array.
29855 bricks: null, //CompositeElement
29856 cols : 0, // array?
29857 // element : null, // wrapped now this.el
29858 _isLayoutInited : null,
29861 getAutoCreate : function(){
29865 cls: 'blog-masonary-wrapper ' + this.cls,
29867 cls : 'mas-boxes masonary'
29874 getChildContainer: function( )
29876 if (this.boxesEl) {
29877 return this.boxesEl;
29880 this.boxesEl = this.el.select('.mas-boxes').first();
29882 return this.boxesEl;
29886 initEvents : function()
29890 if(this.isAutoInitial){
29891 Roo.log('hook children rendered');
29892 this.on('childrenrendered', function() {
29893 Roo.log('children rendered');
29900 initial : function()
29902 this.reloadItems();
29904 this.currentSize = this.el.getBox(true);
29906 /// was window resize... - let's see if this works..
29907 Roo.EventManager.onWindowResize(this.resize, this);
29909 if(!this.isAutoInitial){
29914 this.layout.defer(500,this);
29917 reloadItems: function()
29919 this.bricks = this.el.select('.masonry-brick', true);
29921 this.bricks.each(function(b) {
29922 //Roo.log(b.getSize());
29923 if (!b.attr('originalwidth')) {
29924 b.attr('originalwidth', b.getSize().width);
29929 Roo.log(this.bricks.elements.length);
29932 resize : function()
29935 var cs = this.el.getBox(true);
29937 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29938 Roo.log("no change in with or X");
29941 this.currentSize = cs;
29945 layout : function()
29948 this._resetLayout();
29949 //this._manageStamps();
29951 // don't animate first layout
29952 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29953 this.layoutItems( isInstant );
29955 // flag for initalized
29956 this._isLayoutInited = true;
29959 layoutItems : function( isInstant )
29961 //var items = this._getItemsForLayout( this.items );
29962 // original code supports filtering layout items.. we just ignore it..
29964 this._layoutItems( this.bricks , isInstant );
29966 this._postLayout();
29968 _layoutItems : function ( items , isInstant)
29970 //this.fireEvent( 'layout', this, items );
29973 if ( !items || !items.elements.length ) {
29974 // no items, emit event with empty array
29979 items.each(function(item) {
29980 Roo.log("layout item");
29982 // get x/y object from method
29983 var position = this._getItemLayoutPosition( item );
29985 position.item = item;
29986 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29987 queue.push( position );
29990 this._processLayoutQueue( queue );
29992 /** Sets position of item in DOM
29993 * @param {Element} item
29994 * @param {Number} x - horizontal position
29995 * @param {Number} y - vertical position
29996 * @param {Boolean} isInstant - disables transitions
29998 _processLayoutQueue : function( queue )
30000 for ( var i=0, len = queue.length; i < len; i++ ) {
30001 var obj = queue[i];
30002 obj.item.position('absolute');
30003 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30009 * Any logic you want to do after each layout,
30010 * i.e. size the container
30012 _postLayout : function()
30014 this.resizeContainer();
30017 resizeContainer : function()
30019 if ( !this.isResizingContainer ) {
30022 var size = this._getContainerSize();
30024 this.el.setSize(size.width,size.height);
30025 this.boxesEl.setSize(size.width,size.height);
30031 _resetLayout : function()
30033 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30034 this.colWidth = this.el.getWidth();
30035 //this.gutter = this.el.getWidth();
30037 this.measureColumns();
30043 this.colYs.push( 0 );
30049 measureColumns : function()
30051 this.getContainerWidth();
30052 // if columnWidth is 0, default to outerWidth of first item
30053 if ( !this.columnWidth ) {
30054 var firstItem = this.bricks.first();
30055 Roo.log(firstItem);
30056 this.columnWidth = this.containerWidth;
30057 if (firstItem && firstItem.attr('originalwidth') ) {
30058 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30060 // columnWidth fall back to item of first element
30061 Roo.log("set column width?");
30062 this.initialColumnWidth = this.columnWidth ;
30064 // if first elem has no width, default to size of container
30069 if (this.initialColumnWidth) {
30070 this.columnWidth = this.initialColumnWidth;
30075 // column width is fixed at the top - however if container width get's smaller we should
30078 // this bit calcs how man columns..
30080 var columnWidth = this.columnWidth += this.gutter;
30082 // calculate columns
30083 var containerWidth = this.containerWidth + this.gutter;
30085 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30086 // fix rounding errors, typically with gutters
30087 var excess = columnWidth - containerWidth % columnWidth;
30090 // if overshoot is less than a pixel, round up, otherwise floor it
30091 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30092 cols = Math[ mathMethod ]( cols );
30093 this.cols = Math.max( cols, 1 );
30094 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30096 // padding positioning..
30097 var totalColWidth = this.cols * this.columnWidth;
30098 var padavail = this.containerWidth - totalColWidth;
30099 // so for 2 columns - we need 3 'pads'
30101 var padNeeded = (1+this.cols) * this.padWidth;
30103 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30105 this.columnWidth += padExtra
30106 //this.padWidth = Math.floor(padavail / ( this.cols));
30108 // adjust colum width so that padding is fixed??
30110 // we have 3 columns ... total = width * 3
30111 // we have X left over... that should be used by
30113 //if (this.expandC) {
30121 getContainerWidth : function()
30123 /* // container is parent if fit width
30124 var container = this.isFitWidth ? this.element.parentNode : this.element;
30125 // check that this.size and size are there
30126 // IE8 triggers resize on body size change, so they might not be
30128 var size = getSize( container ); //FIXME
30129 this.containerWidth = size && size.innerWidth; //FIXME
30132 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30136 _getItemLayoutPosition : function( item ) // what is item?
30138 // we resize the item to our columnWidth..
30140 item.setWidth(this.columnWidth);
30141 item.autoBoxAdjust = false;
30143 var sz = item.getSize();
30145 // how many columns does this brick span
30146 var remainder = this.containerWidth % this.columnWidth;
30148 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30149 // round if off by 1 pixel, otherwise use ceil
30150 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30151 colSpan = Math.min( colSpan, this.cols );
30153 // normally this should be '1' as we dont' currently allow multi width columns..
30155 var colGroup = this._getColGroup( colSpan );
30156 // get the minimum Y value from the columns
30157 var minimumY = Math.min.apply( Math, colGroup );
30158 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30160 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30162 // position the brick
30164 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30165 y: this.currentSize.y + minimumY + this.padHeight
30169 // apply setHeight to necessary columns
30170 var setHeight = minimumY + sz.height + this.padHeight;
30171 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30173 var setSpan = this.cols + 1 - colGroup.length;
30174 for ( var i = 0; i < setSpan; i++ ) {
30175 this.colYs[ shortColIndex + i ] = setHeight ;
30182 * @param {Number} colSpan - number of columns the element spans
30183 * @returns {Array} colGroup
30185 _getColGroup : function( colSpan )
30187 if ( colSpan < 2 ) {
30188 // if brick spans only one column, use all the column Ys
30193 // how many different places could this brick fit horizontally
30194 var groupCount = this.cols + 1 - colSpan;
30195 // for each group potential horizontal position
30196 for ( var i = 0; i < groupCount; i++ ) {
30197 // make an array of colY values for that one group
30198 var groupColYs = this.colYs.slice( i, i + colSpan );
30199 // and get the max value of the array
30200 colGroup[i] = Math.max.apply( Math, groupColYs );
30205 _manageStamp : function( stamp )
30207 var stampSize = stamp.getSize();
30208 var offset = stamp.getBox();
30209 // get the columns that this stamp affects
30210 var firstX = this.isOriginLeft ? offset.x : offset.right;
30211 var lastX = firstX + stampSize.width;
30212 var firstCol = Math.floor( firstX / this.columnWidth );
30213 firstCol = Math.max( 0, firstCol );
30215 var lastCol = Math.floor( lastX / this.columnWidth );
30216 // lastCol should not go over if multiple of columnWidth #425
30217 lastCol -= lastX % this.columnWidth ? 0 : 1;
30218 lastCol = Math.min( this.cols - 1, lastCol );
30220 // set colYs to bottom of the stamp
30221 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30224 for ( var i = firstCol; i <= lastCol; i++ ) {
30225 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30230 _getContainerSize : function()
30232 this.maxY = Math.max.apply( Math, this.colYs );
30237 if ( this.isFitWidth ) {
30238 size.width = this._getContainerFitWidth();
30244 _getContainerFitWidth : function()
30246 var unusedCols = 0;
30247 // count unused columns
30250 if ( this.colYs[i] !== 0 ) {
30255 // fit container to columns that have been used
30256 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30259 needsResizeLayout : function()
30261 var previousWidth = this.containerWidth;
30262 this.getContainerWidth();
30263 return previousWidth !== this.containerWidth;
30278 * @class Roo.bootstrap.MasonryBrick
30279 * @extends Roo.bootstrap.Component
30280 * Bootstrap MasonryBrick class
30283 * Create a new MasonryBrick
30284 * @param {Object} config The config object
30287 Roo.bootstrap.MasonryBrick = function(config){
30288 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30294 * When a MasonryBrick is clcik
30295 * @param {Roo.bootstrap.MasonryBrick} this
30296 * @param {Roo.EventObject} e
30302 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30305 * @cfg {String} title
30309 * @cfg {String} html
30313 * @cfg {String} bgimage
30317 * @cfg {String} videourl
30321 * @cfg {String} cls
30325 * @cfg {String} href
30329 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30334 * @cfg {String} (center|bottom) placetitle
30338 getAutoCreate : function()
30340 var cls = 'masonry-brick';
30342 if(this.href.length){
30343 cls += ' masonry-brick-link';
30346 if(this.bgimage.length){
30347 cls += ' masonry-brick-image';
30351 cls += ' masonry-' + this.size + '-brick';
30354 if(this.placetitle.length){
30356 switch (this.placetitle) {
30358 cls += ' masonry-center-title';
30361 cls += ' masonry-bottom-title';
30368 if(!this.html.length && !this.bgimage.length){
30369 cls += ' masonry-center-title';
30372 if(!this.html.length && this.bgimage.length){
30373 cls += ' masonry-bottom-title';
30378 cls += ' ' + this.cls;
30382 tag: (this.href.length) ? 'a' : 'div',
30387 cls: 'masonry-brick-paragraph',
30393 if(this.href.length){
30394 cfg.href = this.href;
30397 var cn = cfg.cn[0].cn;
30399 if(this.title.length){
30402 cls: 'masonry-brick-title',
30407 if(this.html.length){
30410 cls: 'masonry-brick-text',
30414 if (!this.title.length && !this.html.length) {
30415 cfg.cn[0].cls += ' hide';
30418 if(this.bgimage.length){
30421 cls: 'masonry-brick-image-view',
30425 if(this.videourl.length){
30426 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30427 // youtube support only?
30430 cls: 'masonry-brick-image-view',
30433 allowfullscreen : true
30442 initEvents: function()
30444 switch (this.size) {
30446 // this.intSize = 1;
30451 // this.intSize = 2;
30458 // this.intSize = 3;
30463 // this.intSize = 3;
30468 // this.intSize = 3;
30473 // this.intSize = 3;
30485 this.el.on('touchstart', this.onTouchStart, this);
30486 this.el.on('touchmove', this.onTouchMove, this);
30487 this.el.on('touchend', this.onTouchEnd, this);
30488 this.el.on('contextmenu', this.onContextMenu, this);
30490 this.el.on('mouseenter' ,this.enter, this);
30491 this.el.on('mouseleave', this.leave, this);
30494 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30495 this.parent().bricks.push(this);
30500 onClick: function(e, el)
30506 var time = this.endTimer - this.startTimer;
30514 e.preventDefault();
30517 enter: function(e, el)
30519 e.preventDefault();
30521 if(this.bgimage.length && this.html.length){
30522 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30526 leave: function(e, el)
30528 e.preventDefault();
30530 if(this.bgimage.length && this.html.length){
30531 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30535 onTouchStart: function(e, el)
30537 // e.preventDefault();
30539 this.touchmoved = false;
30541 if(!this.bgimage.length || !this.html.length){
30545 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30547 this.timer = new Date().getTime();
30551 onTouchMove: function(e, el)
30553 this.touchmoved = true;
30556 onContextMenu : function(e,el)
30558 e.preventDefault();
30559 e.stopPropagation();
30563 onTouchEnd: function(e, el)
30565 // e.preventDefault();
30567 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30574 if(!this.bgimage.length || !this.html.length){
30576 if(this.href.length){
30577 window.location.href = this.href;
30583 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30585 window.location.href = this.href;
30600 * @class Roo.bootstrap.Brick
30601 * @extends Roo.bootstrap.Component
30602 * Bootstrap Brick class
30605 * Create a new Brick
30606 * @param {Object} config The config object
30609 Roo.bootstrap.Brick = function(config){
30610 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30616 * When a Brick is click
30617 * @param {Roo.bootstrap.Brick} this
30618 * @param {Roo.EventObject} e
30624 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
30627 * @cfg {String} title
30631 * @cfg {String} html
30635 * @cfg {String} bgimage
30639 * @cfg {String} cls
30643 * @cfg {String} href
30647 * @cfg {String} video
30651 * @cfg {Boolean} square
30655 getAutoCreate : function()
30657 var cls = 'roo-brick';
30659 if(this.href.length){
30660 cls += ' roo-brick-link';
30663 if(this.bgimage.length){
30664 cls += ' roo-brick-image';
30667 if(!this.html.length && !this.bgimage.length){
30668 cls += ' roo-brick-center-title';
30671 if(!this.html.length && this.bgimage.length){
30672 cls += ' roo-brick-bottom-title';
30676 cls += ' ' + this.cls;
30680 tag: (this.href.length) ? 'a' : 'div',
30685 cls: 'roo-brick-paragraph',
30691 if(this.href.length){
30692 cfg.href = this.href;
30695 var cn = cfg.cn[0].cn;
30697 if(this.title.length){
30700 cls: 'roo-brick-title',
30705 if(this.html.length){
30708 cls: 'roo-brick-text',
30715 if(this.bgimage.length){
30718 cls: 'roo-brick-image-view',
30726 initEvents: function()
30728 if(this.title.length || this.html.length){
30729 this.el.on('mouseenter' ,this.enter, this);
30730 this.el.on('mouseleave', this.leave, this);
30734 Roo.EventManager.onWindowResize(this.resize, this);
30739 resize : function()
30741 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30743 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30744 // paragraph.setHeight(paragraph.getWidth());
30746 if(this.bgimage.length){
30747 var image = this.el.select('.roo-brick-image-view', true).first();
30748 image.setWidth(paragraph.getWidth());
30749 image.setHeight(paragraph.getWidth());
30754 enter: function(e, el)
30756 e.preventDefault();
30758 if(this.bgimage.length){
30759 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30760 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30764 leave: function(e, el)
30766 e.preventDefault();
30768 if(this.bgimage.length){
30769 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30770 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30780 * Ext JS Library 1.1.1
30781 * Copyright(c) 2006-2007, Ext JS, LLC.
30783 * Originally Released Under LGPL - original licence link has changed is not relivant.
30786 * <script type="text/javascript">
30791 * @class Roo.bootstrap.SplitBar
30792 * @extends Roo.util.Observable
30793 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30797 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30798 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30799 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30800 split.minSize = 100;
30801 split.maxSize = 600;
30802 split.animate = true;
30803 split.on('moved', splitterMoved);
30806 * Create a new SplitBar
30807 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
30808 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
30809 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30810 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
30811 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30812 position of the SplitBar).
30814 Roo.bootstrap.SplitBar = function(cfg){
30819 // dragElement : elm
30820 // resizingElement: el,
30822 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30823 // placement : Roo.bootstrap.SplitBar.LEFT ,
30824 // existingProxy ???
30827 this.el = Roo.get(cfg.dragElement, true);
30828 this.el.dom.unselectable = "on";
30830 this.resizingEl = Roo.get(cfg.resizingElement, true);
30834 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30835 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30838 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30841 * The minimum size of the resizing element. (Defaults to 0)
30847 * The maximum size of the resizing element. (Defaults to 2000)
30850 this.maxSize = 2000;
30853 * Whether to animate the transition to the new size
30856 this.animate = false;
30859 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30862 this.useShim = false;
30867 if(!cfg.existingProxy){
30869 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30871 this.proxy = Roo.get(cfg.existingProxy).dom;
30874 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30877 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30880 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30883 this.dragSpecs = {};
30886 * @private The adapter to use to positon and resize elements
30888 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30889 this.adapter.init(this);
30891 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30893 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30894 this.el.addClass("roo-splitbar-h");
30897 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30898 this.el.addClass("roo-splitbar-v");
30904 * Fires when the splitter is moved (alias for {@link #event-moved})
30905 * @param {Roo.bootstrap.SplitBar} this
30906 * @param {Number} newSize the new width or height
30911 * Fires when the splitter is moved
30912 * @param {Roo.bootstrap.SplitBar} this
30913 * @param {Number} newSize the new width or height
30917 * @event beforeresize
30918 * Fires before the splitter is dragged
30919 * @param {Roo.bootstrap.SplitBar} this
30921 "beforeresize" : true,
30923 "beforeapply" : true
30926 Roo.util.Observable.call(this);
30929 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30930 onStartProxyDrag : function(x, y){
30931 this.fireEvent("beforeresize", this);
30933 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
30935 o.enableDisplayMode("block");
30936 // all splitbars share the same overlay
30937 Roo.bootstrap.SplitBar.prototype.overlay = o;
30939 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30940 this.overlay.show();
30941 Roo.get(this.proxy).setDisplayed("block");
30942 var size = this.adapter.getElementSize(this);
30943 this.activeMinSize = this.getMinimumSize();;
30944 this.activeMaxSize = this.getMaximumSize();;
30945 var c1 = size - this.activeMinSize;
30946 var c2 = Math.max(this.activeMaxSize - size, 0);
30947 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30948 this.dd.resetConstraints();
30949 this.dd.setXConstraint(
30950 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
30951 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30953 this.dd.setYConstraint(0, 0);
30955 this.dd.resetConstraints();
30956 this.dd.setXConstraint(0, 0);
30957 this.dd.setYConstraint(
30958 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
30959 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30962 this.dragSpecs.startSize = size;
30963 this.dragSpecs.startPoint = [x, y];
30964 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30968 * @private Called after the drag operation by the DDProxy
30970 onEndProxyDrag : function(e){
30971 Roo.get(this.proxy).setDisplayed(false);
30972 var endPoint = Roo.lib.Event.getXY(e);
30974 this.overlay.hide();
30977 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30978 newSize = this.dragSpecs.startSize +
30979 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30980 endPoint[0] - this.dragSpecs.startPoint[0] :
30981 this.dragSpecs.startPoint[0] - endPoint[0]
30984 newSize = this.dragSpecs.startSize +
30985 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30986 endPoint[1] - this.dragSpecs.startPoint[1] :
30987 this.dragSpecs.startPoint[1] - endPoint[1]
30990 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30991 if(newSize != this.dragSpecs.startSize){
30992 if(this.fireEvent('beforeapply', this, newSize) !== false){
30993 this.adapter.setElementSize(this, newSize);
30994 this.fireEvent("moved", this, newSize);
30995 this.fireEvent("resize", this, newSize);
31001 * Get the adapter this SplitBar uses
31002 * @return The adapter object
31004 getAdapter : function(){
31005 return this.adapter;
31009 * Set the adapter this SplitBar uses
31010 * @param {Object} adapter A SplitBar adapter object
31012 setAdapter : function(adapter){
31013 this.adapter = adapter;
31014 this.adapter.init(this);
31018 * Gets the minimum size for the resizing element
31019 * @return {Number} The minimum size
31021 getMinimumSize : function(){
31022 return this.minSize;
31026 * Sets the minimum size for the resizing element
31027 * @param {Number} minSize The minimum size
31029 setMinimumSize : function(minSize){
31030 this.minSize = minSize;
31034 * Gets the maximum size for the resizing element
31035 * @return {Number} The maximum size
31037 getMaximumSize : function(){
31038 return this.maxSize;
31042 * Sets the maximum size for the resizing element
31043 * @param {Number} maxSize The maximum size
31045 setMaximumSize : function(maxSize){
31046 this.maxSize = maxSize;
31050 * Sets the initialize size for the resizing element
31051 * @param {Number} size The initial size
31053 setCurrentSize : function(size){
31054 var oldAnimate = this.animate;
31055 this.animate = false;
31056 this.adapter.setElementSize(this, size);
31057 this.animate = oldAnimate;
31061 * Destroy this splitbar.
31062 * @param {Boolean} removeEl True to remove the element
31064 destroy : function(removeEl){
31066 this.shim.remove();
31069 this.proxy.parentNode.removeChild(this.proxy);
31077 * @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.
31079 Roo.bootstrap.SplitBar.createProxy = function(dir){
31080 var proxy = new Roo.Element(document.createElement("div"));
31081 proxy.unselectable();
31082 var cls = 'roo-splitbar-proxy';
31083 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31084 document.body.appendChild(proxy.dom);
31089 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31090 * Default Adapter. It assumes the splitter and resizing element are not positioned
31091 * elements and only gets/sets the width of the element. Generally used for table based layouts.
31093 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31096 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31097 // do nothing for now
31098 init : function(s){
31102 * Called before drag operations to get the current size of the resizing element.
31103 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31105 getElementSize : function(s){
31106 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31107 return s.resizingEl.getWidth();
31109 return s.resizingEl.getHeight();
31114 * Called after drag operations to set the size of the resizing element.
31115 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31116 * @param {Number} newSize The new size to set
31117 * @param {Function} onComplete A function to be invoked when resizing is complete
31119 setElementSize : function(s, newSize, onComplete){
31120 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31122 s.resizingEl.setWidth(newSize);
31124 onComplete(s, newSize);
31127 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31132 s.resizingEl.setHeight(newSize);
31134 onComplete(s, newSize);
31137 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31144 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31145 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31146 * Adapter that moves the splitter element to align with the resized sizing element.
31147 * Used with an absolute positioned SplitBar.
31148 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31149 * document.body, make sure you assign an id to the body element.
31151 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31152 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31153 this.container = Roo.get(container);
31156 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31157 init : function(s){
31158 this.basic.init(s);
31161 getElementSize : function(s){
31162 return this.basic.getElementSize(s);
31165 setElementSize : function(s, newSize, onComplete){
31166 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31169 moveSplitter : function(s){
31170 var yes = Roo.bootstrap.SplitBar;
31171 switch(s.placement){
31173 s.el.setX(s.resizingEl.getRight());
31176 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31179 s.el.setY(s.resizingEl.getBottom());
31182 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31189 * Orientation constant - Create a vertical SplitBar
31193 Roo.bootstrap.SplitBar.VERTICAL = 1;
31196 * Orientation constant - Create a horizontal SplitBar
31200 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31203 * Placement constant - The resizing element is to the left of the splitter element
31207 Roo.bootstrap.SplitBar.LEFT = 1;
31210 * Placement constant - The resizing element is to the right of the splitter element
31214 Roo.bootstrap.SplitBar.RIGHT = 2;
31217 * Placement constant - The resizing element is positioned above the splitter element
31221 Roo.bootstrap.SplitBar.TOP = 3;
31224 * Placement constant - The resizing element is positioned under splitter element
31228 Roo.bootstrap.SplitBar.BOTTOM = 4;
31229 Roo.namespace("Roo.bootstrap.layout");/*
31231 * Ext JS Library 1.1.1
31232 * Copyright(c) 2006-2007, Ext JS, LLC.
31234 * Originally Released Under LGPL - original licence link has changed is not relivant.
31237 * <script type="text/javascript">
31241 * @class Roo.bootstrap.layout.Manager
31242 * @extends Roo.bootstrap.Component
31243 * Base class for layout managers.
31245 Roo.bootstrap.layout.Manager = function(config)
31247 Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31253 /** false to disable window resize monitoring @type Boolean */
31254 this.monitorWindowResize = true;
31259 * Fires when a layout is performed.
31260 * @param {Roo.LayoutManager} this
31264 * @event regionresized
31265 * Fires when the user resizes a region.
31266 * @param {Roo.LayoutRegion} region The resized region
31267 * @param {Number} newSize The new size (width for east/west, height for north/south)
31269 "regionresized" : true,
31271 * @event regioncollapsed
31272 * Fires when a region is collapsed.
31273 * @param {Roo.LayoutRegion} region The collapsed region
31275 "regioncollapsed" : true,
31277 * @event regionexpanded
31278 * Fires when a region is expanded.
31279 * @param {Roo.LayoutRegion} region The expanded region
31281 "regionexpanded" : true
31283 this.updating = false;
31286 this.el = Roo.get(config.el);
31292 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31297 monitorWindowResize : true,
31303 onRender : function(ct, position)
31306 this.el = Roo.get(ct);
31312 initEvents: function()
31316 // ie scrollbar fix
31317 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31318 document.body.scroll = "no";
31319 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31320 this.el.position('relative');
31322 this.id = this.el.id;
31323 this.el.addClass("roo-layout-container");
31324 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31325 if(this.el.dom != document.body ) {
31326 this.el.on('resize', this.layout,this);
31327 this.el.on('show', this.layout,this);
31333 * Returns true if this layout is currently being updated
31334 * @return {Boolean}
31336 isUpdating : function(){
31337 return this.updating;
31341 * Suspend the LayoutManager from doing auto-layouts while
31342 * making multiple add or remove calls
31344 beginUpdate : function(){
31345 this.updating = true;
31349 * Restore auto-layouts and optionally disable the manager from performing a layout
31350 * @param {Boolean} noLayout true to disable a layout update
31352 endUpdate : function(noLayout){
31353 this.updating = false;
31359 layout: function(){
31363 onRegionResized : function(region, newSize){
31364 this.fireEvent("regionresized", region, newSize);
31368 onRegionCollapsed : function(region){
31369 this.fireEvent("regioncollapsed", region);
31372 onRegionExpanded : function(region){
31373 this.fireEvent("regionexpanded", region);
31377 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31378 * performs box-model adjustments.
31379 * @return {Object} The size as an object {width: (the width), height: (the height)}
31381 getViewSize : function()
31384 if(this.el.dom != document.body){
31385 size = this.el.getSize();
31387 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31389 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31390 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31395 * Returns the Element this layout is bound to.
31396 * @return {Roo.Element}
31398 getEl : function(){
31403 * Returns the specified region.
31404 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31405 * @return {Roo.LayoutRegion}
31407 getRegion : function(target){
31408 return this.regions[target.toLowerCase()];
31411 onWindowResize : function(){
31412 if(this.monitorWindowResize){
31418 * Ext JS Library 1.1.1
31419 * Copyright(c) 2006-2007, Ext JS, LLC.
31421 * Originally Released Under LGPL - original licence link has changed is not relivant.
31424 * <script type="text/javascript">
31427 * @class Roo.bootstrap.layout.Border
31428 * @extends Roo.bootstrap.layout.Manager
31429 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31430 * please see: examples/bootstrap/nested.html<br><br>
31432 <b>The container the layout is rendered into can be either the body element or any other element.
31433 If it is not the body element, the container needs to either be an absolute positioned element,
31434 or you will need to add "position:relative" to the css of the container. You will also need to specify
31435 the container size if it is not the body element.</b>
31438 * Create a new Border
31439 * @param {Object} config Configuration options
31441 Roo.bootstrap.layout.Border = function(config){
31442 config = config || {};
31443 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31447 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31448 if(config[region]){
31449 config[region].region = region;
31450 this.addRegion(config[region]);
31456 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
31458 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31460 * Creates and adds a new region if it doesn't already exist.
31461 * @param {String} target The target region key (north, south, east, west or center).
31462 * @param {Object} config The regions config object
31463 * @return {BorderLayoutRegion} The new region
31465 addRegion : function(config)
31467 if(!this.regions[config.region]){
31468 var r = this.factory(config);
31469 this.bindRegion(r);
31471 return this.regions[config.region];
31475 bindRegion : function(r){
31476 this.regions[r.config.region] = r;
31478 r.on("visibilitychange", this.layout, this);
31479 r.on("paneladded", this.layout, this);
31480 r.on("panelremoved", this.layout, this);
31481 r.on("invalidated", this.layout, this);
31482 r.on("resized", this.onRegionResized, this);
31483 r.on("collapsed", this.onRegionCollapsed, this);
31484 r.on("expanded", this.onRegionExpanded, this);
31488 * Performs a layout update.
31490 layout : function()
31492 if(this.updating) {
31495 var size = this.getViewSize();
31496 var w = size.width;
31497 var h = size.height;
31502 //var x = 0, y = 0;
31504 var rs = this.regions;
31505 var north = rs["north"];
31506 var south = rs["south"];
31507 var west = rs["west"];
31508 var east = rs["east"];
31509 var center = rs["center"];
31510 //if(this.hideOnLayout){ // not supported anymore
31511 //c.el.setStyle("display", "none");
31513 if(north && north.isVisible()){
31514 var b = north.getBox();
31515 var m = north.getMargins();
31516 b.width = w - (m.left+m.right);
31519 centerY = b.height + b.y + m.bottom;
31520 centerH -= centerY;
31521 north.updateBox(this.safeBox(b));
31523 if(south && south.isVisible()){
31524 var b = south.getBox();
31525 var m = south.getMargins();
31526 b.width = w - (m.left+m.right);
31528 var totalHeight = (b.height + m.top + m.bottom);
31529 b.y = h - totalHeight + m.top;
31530 centerH -= totalHeight;
31531 south.updateBox(this.safeBox(b));
31533 if(west && west.isVisible()){
31534 var b = west.getBox();
31535 var m = west.getMargins();
31536 b.height = centerH - (m.top+m.bottom);
31538 b.y = centerY + m.top;
31539 var totalWidth = (b.width + m.left + m.right);
31540 centerX += totalWidth;
31541 centerW -= totalWidth;
31542 west.updateBox(this.safeBox(b));
31544 if(east && east.isVisible()){
31545 var b = east.getBox();
31546 var m = east.getMargins();
31547 b.height = centerH - (m.top+m.bottom);
31548 var totalWidth = (b.width + m.left + m.right);
31549 b.x = w - totalWidth + m.left;
31550 b.y = centerY + m.top;
31551 centerW -= totalWidth;
31552 east.updateBox(this.safeBox(b));
31555 var m = center.getMargins();
31557 x: centerX + m.left,
31558 y: centerY + m.top,
31559 width: centerW - (m.left+m.right),
31560 height: centerH - (m.top+m.bottom)
31562 //if(this.hideOnLayout){
31563 //center.el.setStyle("display", "block");
31565 center.updateBox(this.safeBox(centerBox));
31568 this.fireEvent("layout", this);
31572 safeBox : function(box){
31573 box.width = Math.max(0, box.width);
31574 box.height = Math.max(0, box.height);
31579 * Adds a ContentPanel (or subclass) to this layout.
31580 * @param {String} target The target region key (north, south, east, west or center).
31581 * @param {Roo.ContentPanel} panel The panel to add
31582 * @return {Roo.ContentPanel} The added panel
31584 add : function(target, panel){
31586 target = target.toLowerCase();
31587 return this.regions[target].add(panel);
31591 * Remove a ContentPanel (or subclass) to this layout.
31592 * @param {String} target The target region key (north, south, east, west or center).
31593 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31594 * @return {Roo.ContentPanel} The removed panel
31596 remove : function(target, panel){
31597 target = target.toLowerCase();
31598 return this.regions[target].remove(panel);
31602 * Searches all regions for a panel with the specified id
31603 * @param {String} panelId
31604 * @return {Roo.ContentPanel} The panel or null if it wasn't found
31606 findPanel : function(panelId){
31607 var rs = this.regions;
31608 for(var target in rs){
31609 if(typeof rs[target] != "function"){
31610 var p = rs[target].getPanel(panelId);
31620 * Searches all regions for a panel with the specified id and activates (shows) it.
31621 * @param {String/ContentPanel} panelId The panels id or the panel itself
31622 * @return {Roo.ContentPanel} The shown panel or null
31624 showPanel : function(panelId) {
31625 var rs = this.regions;
31626 for(var target in rs){
31627 var r = rs[target];
31628 if(typeof r != "function"){
31629 if(r.hasPanel(panelId)){
31630 return r.showPanel(panelId);
31638 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31639 * @param {Roo.state.Provider} provider (optional) An alternate state provider
31642 restoreState : function(provider){
31644 provider = Roo.state.Manager;
31646 var sm = new Roo.LayoutStateManager();
31647 sm.init(this, provider);
31653 * Adds a xtype elements to the layout.
31657 xtype : 'ContentPanel',
31664 xtype : 'NestedLayoutPanel',
31670 items : [ ... list of content panels or nested layout panels.. ]
31674 * @param {Object} cfg Xtype definition of item to add.
31676 addxtype : function(cfg)
31678 // basically accepts a pannel...
31679 // can accept a layout region..!?!?
31680 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31683 // theory? children can only be panels??
31685 //if (!cfg.xtype.match(/Panel$/)) {
31690 if (typeof(cfg.region) == 'undefined') {
31691 Roo.log("Failed to add Panel, region was not set");
31695 var region = cfg.region;
31701 xitems = cfg.items;
31708 case 'Content': // ContentPanel (el, cfg)
31709 case 'Scroll': // ContentPanel (el, cfg)
31711 cfg.autoCreate = true;
31712 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31714 // var el = this.el.createChild();
31715 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31718 this.add(region, ret);
31722 case 'TreePanel': // our new panel!
31723 cfg.el = this.el.createChild();
31724 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31725 this.add(region, ret);
31730 // create a new Layout (which is a Border Layout...
31732 var clayout = cfg.layout;
31733 clayout.el = this.el.createChild();
31734 clayout.items = clayout.items || [];
31738 // replace this exitems with the clayout ones..
31739 xitems = clayout.items;
31741 // force background off if it's in center...
31742 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31743 cfg.background = false;
31745 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
31748 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31749 //console.log('adding nested layout panel ' + cfg.toSource());
31750 this.add(region, ret);
31751 nb = {}; /// find first...
31756 // needs grid and region
31758 //var el = this.getRegion(region).el.createChild();
31760 *var el = this.el.createChild();
31761 // create the grid first...
31762 cfg.grid.container = el;
31763 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31766 if (region == 'center' && this.active ) {
31767 cfg.background = false;
31770 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31772 this.add(region, ret);
31774 if (cfg.background) {
31775 // render grid on panel activation (if panel background)
31776 ret.on('activate', function(gp) {
31777 if (!gp.grid.rendered) {
31778 // gp.grid.render(el);
31782 // cfg.grid.render(el);
31788 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31789 // it was the old xcomponent building that caused this before.
31790 // espeically if border is the top element in the tree.
31800 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31802 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31803 this.add(region, ret);
31807 throw "Can not add '" + cfg.xtype + "' to Border";
31813 this.beginUpdate();
31817 Roo.each(xitems, function(i) {
31818 region = nb && i.region ? i.region : false;
31820 var add = ret.addxtype(i);
31823 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31824 if (!i.background) {
31825 abn[region] = nb[region] ;
31832 // make the last non-background panel active..
31833 //if (nb) { Roo.log(abn); }
31836 for(var r in abn) {
31837 region = this.getRegion(r);
31839 // tried using nb[r], but it does not work..
31841 region.showPanel(abn[r]);
31852 factory : function(cfg)
31855 var validRegions = Roo.bootstrap.layout.Border.regions;
31857 var target = cfg.region;
31860 var r = Roo.bootstrap.layout;
31864 return new r.North(cfg);
31866 return new r.South(cfg);
31868 return new r.East(cfg);
31870 return new r.West(cfg);
31872 return new r.Center(cfg);
31874 throw 'Layout region "'+target+'" not supported.';
31881 * Ext JS Library 1.1.1
31882 * Copyright(c) 2006-2007, Ext JS, LLC.
31884 * Originally Released Under LGPL - original licence link has changed is not relivant.
31887 * <script type="text/javascript">
31891 * @class Roo.bootstrap.layout.Basic
31892 * @extends Roo.util.Observable
31893 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31894 * and does not have a titlebar, tabs or any other features. All it does is size and position
31895 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31896 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
31897 * @cfg {string} region the region that it inhabits..
31898 * @cfg {bool} skipConfig skip config?
31902 Roo.bootstrap.layout.Basic = function(config){
31904 this.mgr = config.mgr;
31906 this.position = config.region;
31908 var skipConfig = config.skipConfig;
31912 * @scope Roo.BasicLayoutRegion
31916 * @event beforeremove
31917 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31918 * @param {Roo.LayoutRegion} this
31919 * @param {Roo.ContentPanel} panel The panel
31920 * @param {Object} e The cancel event object
31922 "beforeremove" : true,
31924 * @event invalidated
31925 * Fires when the layout for this region is changed.
31926 * @param {Roo.LayoutRegion} this
31928 "invalidated" : true,
31930 * @event visibilitychange
31931 * Fires when this region is shown or hidden
31932 * @param {Roo.LayoutRegion} this
31933 * @param {Boolean} visibility true or false
31935 "visibilitychange" : true,
31937 * @event paneladded
31938 * Fires when a panel is added.
31939 * @param {Roo.LayoutRegion} this
31940 * @param {Roo.ContentPanel} panel The panel
31942 "paneladded" : true,
31944 * @event panelremoved
31945 * Fires when a panel is removed.
31946 * @param {Roo.LayoutRegion} this
31947 * @param {Roo.ContentPanel} panel The panel
31949 "panelremoved" : true,
31951 * @event beforecollapse
31952 * Fires when this region before collapse.
31953 * @param {Roo.LayoutRegion} this
31955 "beforecollapse" : true,
31958 * Fires when this region is collapsed.
31959 * @param {Roo.LayoutRegion} this
31961 "collapsed" : true,
31964 * Fires when this region is expanded.
31965 * @param {Roo.LayoutRegion} this
31970 * Fires when this region is slid into view.
31971 * @param {Roo.LayoutRegion} this
31973 "slideshow" : true,
31976 * Fires when this region slides out of view.
31977 * @param {Roo.LayoutRegion} this
31979 "slidehide" : true,
31981 * @event panelactivated
31982 * Fires when a panel is activated.
31983 * @param {Roo.LayoutRegion} this
31984 * @param {Roo.ContentPanel} panel The activated panel
31986 "panelactivated" : true,
31989 * Fires when the user resizes this region.
31990 * @param {Roo.LayoutRegion} this
31991 * @param {Number} newSize The new size (width for east/west, height for north/south)
31995 /** A collection of panels in this region. @type Roo.util.MixedCollection */
31996 this.panels = new Roo.util.MixedCollection();
31997 this.panels.getKey = this.getPanelId.createDelegate(this);
31999 this.activePanel = null;
32000 // ensure listeners are added...
32002 if (config.listeners || config.events) {
32003 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32004 listeners : config.listeners || {},
32005 events : config.events || {}
32009 if(skipConfig !== true){
32010 this.applyConfig(config);
32014 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32016 getPanelId : function(p){
32020 applyConfig : function(config){
32021 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32022 this.config = config;
32027 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
32028 * the width, for horizontal (north, south) the height.
32029 * @param {Number} newSize The new width or height
32031 resizeTo : function(newSize){
32032 var el = this.el ? this.el :
32033 (this.activePanel ? this.activePanel.getEl() : null);
32035 switch(this.position){
32038 el.setWidth(newSize);
32039 this.fireEvent("resized", this, newSize);
32043 el.setHeight(newSize);
32044 this.fireEvent("resized", this, newSize);
32050 getBox : function(){
32051 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32054 getMargins : function(){
32055 return this.margins;
32058 updateBox : function(box){
32060 var el = this.activePanel.getEl();
32061 el.dom.style.left = box.x + "px";
32062 el.dom.style.top = box.y + "px";
32063 this.activePanel.setSize(box.width, box.height);
32067 * Returns the container element for this region.
32068 * @return {Roo.Element}
32070 getEl : function(){
32071 return this.activePanel;
32075 * Returns true if this region is currently visible.
32076 * @return {Boolean}
32078 isVisible : function(){
32079 return this.activePanel ? true : false;
32082 setActivePanel : function(panel){
32083 panel = this.getPanel(panel);
32084 if(this.activePanel && this.activePanel != panel){
32085 this.activePanel.setActiveState(false);
32086 this.activePanel.getEl().setLeftTop(-10000,-10000);
32088 this.activePanel = panel;
32089 panel.setActiveState(true);
32091 panel.setSize(this.box.width, this.box.height);
32093 this.fireEvent("panelactivated", this, panel);
32094 this.fireEvent("invalidated");
32098 * Show the specified panel.
32099 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32100 * @return {Roo.ContentPanel} The shown panel or null
32102 showPanel : function(panel){
32103 panel = this.getPanel(panel);
32105 this.setActivePanel(panel);
32111 * Get the active panel for this region.
32112 * @return {Roo.ContentPanel} The active panel or null
32114 getActivePanel : function(){
32115 return this.activePanel;
32119 * Add the passed ContentPanel(s)
32120 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32121 * @return {Roo.ContentPanel} The panel added (if only one was added)
32123 add : function(panel){
32124 if(arguments.length > 1){
32125 for(var i = 0, len = arguments.length; i < len; i++) {
32126 this.add(arguments[i]);
32130 if(this.hasPanel(panel)){
32131 this.showPanel(panel);
32134 var el = panel.getEl();
32135 if(el.dom.parentNode != this.mgr.el.dom){
32136 this.mgr.el.dom.appendChild(el.dom);
32138 if(panel.setRegion){
32139 panel.setRegion(this);
32141 this.panels.add(panel);
32142 el.setStyle("position", "absolute");
32143 if(!panel.background){
32144 this.setActivePanel(panel);
32145 if(this.config.initialSize && this.panels.getCount()==1){
32146 this.resizeTo(this.config.initialSize);
32149 this.fireEvent("paneladded", this, panel);
32154 * Returns true if the panel is in this region.
32155 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32156 * @return {Boolean}
32158 hasPanel : function(panel){
32159 if(typeof panel == "object"){ // must be panel obj
32160 panel = panel.getId();
32162 return this.getPanel(panel) ? true : false;
32166 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32167 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32168 * @param {Boolean} preservePanel Overrides the config preservePanel option
32169 * @return {Roo.ContentPanel} The panel that was removed
32171 remove : function(panel, preservePanel){
32172 panel = this.getPanel(panel);
32177 this.fireEvent("beforeremove", this, panel, e);
32178 if(e.cancel === true){
32181 var panelId = panel.getId();
32182 this.panels.removeKey(panelId);
32187 * Returns the panel specified or null if it's not in this region.
32188 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32189 * @return {Roo.ContentPanel}
32191 getPanel : function(id){
32192 if(typeof id == "object"){ // must be panel obj
32195 return this.panels.get(id);
32199 * Returns this regions position (north/south/east/west/center).
32202 getPosition: function(){
32203 return this.position;
32207 * Ext JS Library 1.1.1
32208 * Copyright(c) 2006-2007, Ext JS, LLC.
32210 * Originally Released Under LGPL - original licence link has changed is not relivant.
32213 * <script type="text/javascript">
32217 * @class Roo.bootstrap.layout.Region
32218 * @extends Roo.bootstrap.layout.Basic
32219 * This class represents a region in a layout manager.
32221 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32222 * @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})
32223 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
32224 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
32225 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
32226 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
32227 * @cfg {String} title The title for the region (overrides panel titles)
32228 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
32229 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32230 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
32231 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32232 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
32233 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32234 * the space available, similar to FireFox 1.5 tabs (defaults to false)
32235 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
32236 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
32237 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
32239 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
32240 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
32241 * @cfg {Boolean} disableTabTips True to disable tab tooltips
32242 * @cfg {Number} width For East/West panels
32243 * @cfg {Number} height For North/South panels
32244 * @cfg {Boolean} split To show the splitter
32245 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
32247 * @cfg {string} cls Extra CSS classes to add to region
32249 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32250 * @cfg {string} region the region that it inhabits..
32253 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
32254 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
32256 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
32257 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
32258 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
32260 Roo.bootstrap.layout.Region = function(config)
32262 this.applyConfig(config);
32264 var mgr = config.mgr;
32265 var pos = config.region;
32266 config.skipConfig = true;
32267 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32270 this.onRender(mgr.el);
32273 this.visible = true;
32274 this.collapsed = false;
32277 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32279 position: '', // set by wrapper (eg. north/south etc..)
32281 createBody : function(){
32282 /** This region's body element
32283 * @type Roo.Element */
32284 this.bodyEl = this.el.createChild({
32286 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32290 onRender: function(ctr, pos)
32292 var dh = Roo.DomHelper;
32293 /** This region's container element
32294 * @type Roo.Element */
32295 this.el = dh.append(ctr.dom, {
32297 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32299 /** This region's title element
32300 * @type Roo.Element */
32302 this.titleEl = dh.append(this.el.dom,
32305 unselectable: "on",
32306 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32308 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
32309 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32312 this.titleEl.enableDisplayMode();
32313 /** This region's title text element
32314 * @type HTMLElement */
32315 this.titleTextEl = this.titleEl.dom.firstChild;
32316 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32318 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32319 this.closeBtn.enableDisplayMode();
32320 this.closeBtn.on("click", this.closeClicked, this);
32321 this.closeBtn.hide();
32323 this.createBody(this.config);
32324 if(this.config.hideWhenEmpty){
32326 this.on("paneladded", this.validateVisibility, this);
32327 this.on("panelremoved", this.validateVisibility, this);
32329 if(this.autoScroll){
32330 this.bodyEl.setStyle("overflow", "auto");
32332 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32334 //if(c.titlebar !== false){
32335 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32336 this.titleEl.hide();
32338 this.titleEl.show();
32339 if(this.config.title){
32340 this.titleTextEl.innerHTML = this.config.title;
32344 if(this.config.collapsed){
32345 this.collapse(true);
32347 if(this.config.hidden){
32352 applyConfig : function(c)
32355 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32356 var dh = Roo.DomHelper;
32357 if(c.titlebar !== false){
32358 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32359 this.collapseBtn.on("click", this.collapse, this);
32360 this.collapseBtn.enableDisplayMode();
32362 if(c.showPin === true || this.showPin){
32363 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32364 this.stickBtn.enableDisplayMode();
32365 this.stickBtn.on("click", this.expand, this);
32366 this.stickBtn.hide();
32371 /** This region's collapsed element
32372 * @type Roo.Element */
32375 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32376 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32379 if(c.floatable !== false){
32380 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32381 this.collapsedEl.on("click", this.collapseClick, this);
32384 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32385 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32386 id: "message", unselectable: "on", style:{"float":"left"}});
32387 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32389 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32390 this.expandBtn.on("click", this.expand, this);
32394 if(this.collapseBtn){
32395 this.collapseBtn.setVisible(c.collapsible == true);
32398 this.cmargins = c.cmargins || this.cmargins ||
32399 (this.position == "west" || this.position == "east" ?
32400 {top: 0, left: 2, right:2, bottom: 0} :
32401 {top: 2, left: 0, right:0, bottom: 2});
32403 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32406 this.bottomTabs = c.tabPosition != "top";
32408 this.autoScroll = c.autoScroll || false;
32413 this.duration = c.duration || .30;
32414 this.slideDuration = c.slideDuration || .45;
32419 * Returns true if this region is currently visible.
32420 * @return {Boolean}
32422 isVisible : function(){
32423 return this.visible;
32427 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32428 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
32430 //setCollapsedTitle : function(title){
32431 // title = title || " ";
32432 // if(this.collapsedTitleTextEl){
32433 // this.collapsedTitleTextEl.innerHTML = title;
32437 getBox : function(){
32439 // if(!this.collapsed){
32440 b = this.el.getBox(false, true);
32442 // b = this.collapsedEl.getBox(false, true);
32447 getMargins : function(){
32448 return this.margins;
32449 //return this.collapsed ? this.cmargins : this.margins;
32452 highlight : function(){
32453 this.el.addClass("x-layout-panel-dragover");
32456 unhighlight : function(){
32457 this.el.removeClass("x-layout-panel-dragover");
32460 updateBox : function(box)
32463 if(!this.collapsed){
32464 this.el.dom.style.left = box.x + "px";
32465 this.el.dom.style.top = box.y + "px";
32466 this.updateBody(box.width, box.height);
32468 this.collapsedEl.dom.style.left = box.x + "px";
32469 this.collapsedEl.dom.style.top = box.y + "px";
32470 this.collapsedEl.setSize(box.width, box.height);
32473 this.tabs.autoSizeTabs();
32477 updateBody : function(w, h)
32480 this.el.setWidth(w);
32481 w -= this.el.getBorderWidth("rl");
32482 if(this.config.adjustments){
32483 w += this.config.adjustments[0];
32487 this.el.setHeight(h);
32488 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32489 h -= this.el.getBorderWidth("tb");
32490 if(this.config.adjustments){
32491 h += this.config.adjustments[1];
32493 this.bodyEl.setHeight(h);
32495 h = this.tabs.syncHeight(h);
32498 if(this.panelSize){
32499 w = w !== null ? w : this.panelSize.width;
32500 h = h !== null ? h : this.panelSize.height;
32502 if(this.activePanel){
32503 var el = this.activePanel.getEl();
32504 w = w !== null ? w : el.getWidth();
32505 h = h !== null ? h : el.getHeight();
32506 this.panelSize = {width: w, height: h};
32507 this.activePanel.setSize(w, h);
32509 if(Roo.isIE && this.tabs){
32510 this.tabs.el.repaint();
32515 * Returns the container element for this region.
32516 * @return {Roo.Element}
32518 getEl : function(){
32523 * Hides this region.
32526 //if(!this.collapsed){
32527 this.el.dom.style.left = "-2000px";
32530 // this.collapsedEl.dom.style.left = "-2000px";
32531 // this.collapsedEl.hide();
32533 this.visible = false;
32534 this.fireEvent("visibilitychange", this, false);
32538 * Shows this region if it was previously hidden.
32541 //if(!this.collapsed){
32544 // this.collapsedEl.show();
32546 this.visible = true;
32547 this.fireEvent("visibilitychange", this, true);
32550 closeClicked : function(){
32551 if(this.activePanel){
32552 this.remove(this.activePanel);
32556 collapseClick : function(e){
32558 e.stopPropagation();
32561 e.stopPropagation();
32567 * Collapses this region.
32568 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32571 collapse : function(skipAnim, skipCheck = false){
32572 if(this.collapsed) {
32576 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32578 this.collapsed = true;
32580 this.split.el.hide();
32582 if(this.config.animate && skipAnim !== true){
32583 this.fireEvent("invalidated", this);
32584 this.animateCollapse();
32586 this.el.setLocation(-20000,-20000);
32588 this.collapsedEl.show();
32589 this.fireEvent("collapsed", this);
32590 this.fireEvent("invalidated", this);
32596 animateCollapse : function(){
32601 * Expands this region if it was previously collapsed.
32602 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32603 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32606 expand : function(e, skipAnim){
32608 e.stopPropagation();
32610 if(!this.collapsed || this.el.hasActiveFx()) {
32614 this.afterSlideIn();
32617 this.collapsed = false;
32618 if(this.config.animate && skipAnim !== true){
32619 this.animateExpand();
32623 this.split.el.show();
32625 this.collapsedEl.setLocation(-2000,-2000);
32626 this.collapsedEl.hide();
32627 this.fireEvent("invalidated", this);
32628 this.fireEvent("expanded", this);
32632 animateExpand : function(){
32636 initTabs : function()
32638 this.bodyEl.setStyle("overflow", "hidden");
32639 var ts = new Roo.bootstrap.panel.Tabs({
32640 el: this.bodyEl.dom,
32641 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32642 disableTooltips: this.config.disableTabTips,
32643 toolbar : this.config.toolbar
32646 if(this.config.hideTabs){
32647 ts.stripWrap.setDisplayed(false);
32650 ts.resizeTabs = this.config.resizeTabs === true;
32651 ts.minTabWidth = this.config.minTabWidth || 40;
32652 ts.maxTabWidth = this.config.maxTabWidth || 250;
32653 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32654 ts.monitorResize = false;
32655 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32656 ts.bodyEl.addClass('roo-layout-tabs-body');
32657 this.panels.each(this.initPanelAsTab, this);
32660 initPanelAsTab : function(panel){
32661 var ti = this.tabs.addTab(
32663 panel.getTitle(), null,
32664 this.config.closeOnTab && panel.isClosable()
32666 if(panel.tabTip !== undefined){
32667 ti.setTooltip(panel.tabTip);
32669 ti.on("activate", function(){
32670 this.setActivePanel(panel);
32673 if(this.config.closeOnTab){
32674 ti.on("beforeclose", function(t, e){
32676 this.remove(panel);
32682 updatePanelTitle : function(panel, title)
32684 if(this.activePanel == panel){
32685 this.updateTitle(title);
32688 var ti = this.tabs.getTab(panel.getEl().id);
32690 if(panel.tabTip !== undefined){
32691 ti.setTooltip(panel.tabTip);
32696 updateTitle : function(title){
32697 if(this.titleTextEl && !this.config.title){
32698 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
32702 setActivePanel : function(panel)
32704 panel = this.getPanel(panel);
32705 if(this.activePanel && this.activePanel != panel){
32706 this.activePanel.setActiveState(false);
32708 this.activePanel = panel;
32709 panel.setActiveState(true);
32710 if(this.panelSize){
32711 panel.setSize(this.panelSize.width, this.panelSize.height);
32714 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32716 this.updateTitle(panel.getTitle());
32718 this.fireEvent("invalidated", this);
32720 this.fireEvent("panelactivated", this, panel);
32724 * Shows the specified panel.
32725 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32726 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32728 showPanel : function(panel)
32730 panel = this.getPanel(panel);
32733 var tab = this.tabs.getTab(panel.getEl().id);
32734 if(tab.isHidden()){
32735 this.tabs.unhideTab(tab.id);
32739 this.setActivePanel(panel);
32746 * Get the active panel for this region.
32747 * @return {Roo.ContentPanel} The active panel or null
32749 getActivePanel : function(){
32750 return this.activePanel;
32753 validateVisibility : function(){
32754 if(this.panels.getCount() < 1){
32755 this.updateTitle(" ");
32756 this.closeBtn.hide();
32759 if(!this.isVisible()){
32766 * Adds the passed ContentPanel(s) to this region.
32767 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32768 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32770 add : function(panel){
32771 if(arguments.length > 1){
32772 for(var i = 0, len = arguments.length; i < len; i++) {
32773 this.add(arguments[i]);
32777 if(this.hasPanel(panel)){
32778 this.showPanel(panel);
32781 panel.setRegion(this);
32782 this.panels.add(panel);
32783 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32784 this.bodyEl.dom.appendChild(panel.getEl().dom);
32785 if(panel.background !== true){
32786 this.setActivePanel(panel);
32788 this.fireEvent("paneladded", this, panel);
32794 this.initPanelAsTab(panel);
32798 if(panel.background !== true){
32799 this.tabs.activate(panel.getEl().id);
32801 this.fireEvent("paneladded", this, panel);
32806 * Hides the tab for the specified panel.
32807 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32809 hidePanel : function(panel){
32810 if(this.tabs && (panel = this.getPanel(panel))){
32811 this.tabs.hideTab(panel.getEl().id);
32816 * Unhides the tab for a previously hidden panel.
32817 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32819 unhidePanel : function(panel){
32820 if(this.tabs && (panel = this.getPanel(panel))){
32821 this.tabs.unhideTab(panel.getEl().id);
32825 clearPanels : function(){
32826 while(this.panels.getCount() > 0){
32827 this.remove(this.panels.first());
32832 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32833 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32834 * @param {Boolean} preservePanel Overrides the config preservePanel option
32835 * @return {Roo.ContentPanel} The panel that was removed
32837 remove : function(panel, preservePanel)
32839 panel = this.getPanel(panel);
32844 this.fireEvent("beforeremove", this, panel, e);
32845 if(e.cancel === true){
32848 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32849 var panelId = panel.getId();
32850 this.panels.removeKey(panelId);
32852 document.body.appendChild(panel.getEl().dom);
32855 this.tabs.removeTab(panel.getEl().id);
32856 }else if (!preservePanel){
32857 this.bodyEl.dom.removeChild(panel.getEl().dom);
32859 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32860 var p = this.panels.first();
32861 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32862 tempEl.appendChild(p.getEl().dom);
32863 this.bodyEl.update("");
32864 this.bodyEl.dom.appendChild(p.getEl().dom);
32866 this.updateTitle(p.getTitle());
32868 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32869 this.setActivePanel(p);
32871 panel.setRegion(null);
32872 if(this.activePanel == panel){
32873 this.activePanel = null;
32875 if(this.config.autoDestroy !== false && preservePanel !== true){
32876 try{panel.destroy();}catch(e){}
32878 this.fireEvent("panelremoved", this, panel);
32883 * Returns the TabPanel component used by this region
32884 * @return {Roo.TabPanel}
32886 getTabs : function(){
32890 createTool : function(parentEl, className){
32891 var btn = Roo.DomHelper.append(parentEl, {
32893 cls: "x-layout-tools-button",
32896 cls: "roo-layout-tools-button-inner " + className,
32900 btn.addClassOnOver("roo-layout-tools-button-over");
32905 * Ext JS Library 1.1.1
32906 * Copyright(c) 2006-2007, Ext JS, LLC.
32908 * Originally Released Under LGPL - original licence link has changed is not relivant.
32911 * <script type="text/javascript">
32917 * @class Roo.SplitLayoutRegion
32918 * @extends Roo.LayoutRegion
32919 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32921 Roo.bootstrap.layout.Split = function(config){
32922 this.cursor = config.cursor;
32923 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32926 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32928 splitTip : "Drag to resize.",
32929 collapsibleSplitTip : "Drag to resize. Double click to hide.",
32930 useSplitTips : false,
32932 applyConfig : function(config){
32933 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32936 onRender : function(ctr,pos) {
32938 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
32939 if(!this.config.split){
32944 var splitEl = Roo.DomHelper.append(ctr.dom, {
32946 id: this.el.id + "-split",
32947 cls: "roo-layout-split roo-layout-split-"+this.position,
32950 /** The SplitBar for this region
32951 * @type Roo.SplitBar */
32952 // does not exist yet...
32953 Roo.log([this.position, this.orientation]);
32955 this.split = new Roo.bootstrap.SplitBar({
32956 dragElement : splitEl,
32957 resizingElement: this.el,
32958 orientation : this.orientation
32961 this.split.on("moved", this.onSplitMove, this);
32962 this.split.useShim = this.config.useShim === true;
32963 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32964 if(this.useSplitTips){
32965 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32967 //if(config.collapsible){
32968 // this.split.el.on("dblclick", this.collapse, this);
32971 if(typeof this.config.minSize != "undefined"){
32972 this.split.minSize = this.config.minSize;
32974 if(typeof this.config.maxSize != "undefined"){
32975 this.split.maxSize = this.config.maxSize;
32977 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
32978 this.hideSplitter();
32983 getHMaxSize : function(){
32984 var cmax = this.config.maxSize || 10000;
32985 var center = this.mgr.getRegion("center");
32986 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32989 getVMaxSize : function(){
32990 var cmax = this.config.maxSize || 10000;
32991 var center = this.mgr.getRegion("center");
32992 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32995 onSplitMove : function(split, newSize){
32996 this.fireEvent("resized", this, newSize);
33000 * Returns the {@link Roo.SplitBar} for this region.
33001 * @return {Roo.SplitBar}
33003 getSplitBar : function(){
33008 this.hideSplitter();
33009 Roo.bootstrap.layout.Split.superclass.hide.call(this);
33012 hideSplitter : function(){
33014 this.split.el.setLocation(-2000,-2000);
33015 this.split.el.hide();
33021 this.split.el.show();
33023 Roo.bootstrap.layout.Split.superclass.show.call(this);
33026 beforeSlide: function(){
33027 if(Roo.isGecko){// firefox overflow auto bug workaround
33028 this.bodyEl.clip();
33030 this.tabs.bodyEl.clip();
33032 if(this.activePanel){
33033 this.activePanel.getEl().clip();
33035 if(this.activePanel.beforeSlide){
33036 this.activePanel.beforeSlide();
33042 afterSlide : function(){
33043 if(Roo.isGecko){// firefox overflow auto bug workaround
33044 this.bodyEl.unclip();
33046 this.tabs.bodyEl.unclip();
33048 if(this.activePanel){
33049 this.activePanel.getEl().unclip();
33050 if(this.activePanel.afterSlide){
33051 this.activePanel.afterSlide();
33057 initAutoHide : function(){
33058 if(this.autoHide !== false){
33059 if(!this.autoHideHd){
33060 var st = new Roo.util.DelayedTask(this.slideIn, this);
33061 this.autoHideHd = {
33062 "mouseout": function(e){
33063 if(!e.within(this.el, true)){
33067 "mouseover" : function(e){
33073 this.el.on(this.autoHideHd);
33077 clearAutoHide : function(){
33078 if(this.autoHide !== false){
33079 this.el.un("mouseout", this.autoHideHd.mouseout);
33080 this.el.un("mouseover", this.autoHideHd.mouseover);
33084 clearMonitor : function(){
33085 Roo.get(document).un("click", this.slideInIf, this);
33088 // these names are backwards but not changed for compat
33089 slideOut : function(){
33090 if(this.isSlid || this.el.hasActiveFx()){
33093 this.isSlid = true;
33094 if(this.collapseBtn){
33095 this.collapseBtn.hide();
33097 this.closeBtnState = this.closeBtn.getStyle('display');
33098 this.closeBtn.hide();
33100 this.stickBtn.show();
33103 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33104 this.beforeSlide();
33105 this.el.setStyle("z-index", 10001);
33106 this.el.slideIn(this.getSlideAnchor(), {
33107 callback: function(){
33109 this.initAutoHide();
33110 Roo.get(document).on("click", this.slideInIf, this);
33111 this.fireEvent("slideshow", this);
33118 afterSlideIn : function(){
33119 this.clearAutoHide();
33120 this.isSlid = false;
33121 this.clearMonitor();
33122 this.el.setStyle("z-index", "");
33123 if(this.collapseBtn){
33124 this.collapseBtn.show();
33126 this.closeBtn.setStyle('display', this.closeBtnState);
33128 this.stickBtn.hide();
33130 this.fireEvent("slidehide", this);
33133 slideIn : function(cb){
33134 if(!this.isSlid || this.el.hasActiveFx()){
33138 this.isSlid = false;
33139 this.beforeSlide();
33140 this.el.slideOut(this.getSlideAnchor(), {
33141 callback: function(){
33142 this.el.setLeftTop(-10000, -10000);
33144 this.afterSlideIn();
33152 slideInIf : function(e){
33153 if(!e.within(this.el)){
33158 animateCollapse : function(){
33159 this.beforeSlide();
33160 this.el.setStyle("z-index", 20000);
33161 var anchor = this.getSlideAnchor();
33162 this.el.slideOut(anchor, {
33163 callback : function(){
33164 this.el.setStyle("z-index", "");
33165 this.collapsedEl.slideIn(anchor, {duration:.3});
33167 this.el.setLocation(-10000,-10000);
33169 this.fireEvent("collapsed", this);
33176 animateExpand : function(){
33177 this.beforeSlide();
33178 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33179 this.el.setStyle("z-index", 20000);
33180 this.collapsedEl.hide({
33183 this.el.slideIn(this.getSlideAnchor(), {
33184 callback : function(){
33185 this.el.setStyle("z-index", "");
33188 this.split.el.show();
33190 this.fireEvent("invalidated", this);
33191 this.fireEvent("expanded", this);
33219 getAnchor : function(){
33220 return this.anchors[this.position];
33223 getCollapseAnchor : function(){
33224 return this.canchors[this.position];
33227 getSlideAnchor : function(){
33228 return this.sanchors[this.position];
33231 getAlignAdj : function(){
33232 var cm = this.cmargins;
33233 switch(this.position){
33249 getExpandAdj : function(){
33250 var c = this.collapsedEl, cm = this.cmargins;
33251 switch(this.position){
33253 return [-(cm.right+c.getWidth()+cm.left), 0];
33256 return [cm.right+c.getWidth()+cm.left, 0];
33259 return [0, -(cm.top+cm.bottom+c.getHeight())];
33262 return [0, cm.top+cm.bottom+c.getHeight()];
33268 * Ext JS Library 1.1.1
33269 * Copyright(c) 2006-2007, Ext JS, LLC.
33271 * Originally Released Under LGPL - original licence link has changed is not relivant.
33274 * <script type="text/javascript">
33277 * These classes are private internal classes
33279 Roo.bootstrap.layout.Center = function(config){
33280 config.region = "center";
33281 Roo.bootstrap.layout.Region.call(this, config);
33282 this.visible = true;
33283 this.minWidth = config.minWidth || 20;
33284 this.minHeight = config.minHeight || 20;
33287 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33289 // center panel can't be hidden
33293 // center panel can't be hidden
33296 getMinWidth: function(){
33297 return this.minWidth;
33300 getMinHeight: function(){
33301 return this.minHeight;
33314 Roo.bootstrap.layout.North = function(config)
33316 config.region = 'north';
33317 config.cursor = 'n-resize';
33319 Roo.bootstrap.layout.Split.call(this, config);
33323 this.split.placement = Roo.bootstrap.SplitBar.TOP;
33324 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33325 this.split.el.addClass("roo-layout-split-v");
33327 var size = config.initialSize || config.height;
33328 if(typeof size != "undefined"){
33329 this.el.setHeight(size);
33332 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33334 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33338 getBox : function(){
33339 if(this.collapsed){
33340 return this.collapsedEl.getBox();
33342 var box = this.el.getBox();
33344 box.height += this.split.el.getHeight();
33349 updateBox : function(box){
33350 if(this.split && !this.collapsed){
33351 box.height -= this.split.el.getHeight();
33352 this.split.el.setLeft(box.x);
33353 this.split.el.setTop(box.y+box.height);
33354 this.split.el.setWidth(box.width);
33356 if(this.collapsed){
33357 this.updateBody(box.width, null);
33359 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33367 Roo.bootstrap.layout.South = function(config){
33368 config.region = 'south';
33369 config.cursor = 's-resize';
33370 Roo.bootstrap.layout.Split.call(this, config);
33372 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33373 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33374 this.split.el.addClass("roo-layout-split-v");
33376 var size = config.initialSize || config.height;
33377 if(typeof size != "undefined"){
33378 this.el.setHeight(size);
33382 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33383 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33384 getBox : function(){
33385 if(this.collapsed){
33386 return this.collapsedEl.getBox();
33388 var box = this.el.getBox();
33390 var sh = this.split.el.getHeight();
33397 updateBox : function(box){
33398 if(this.split && !this.collapsed){
33399 var sh = this.split.el.getHeight();
33402 this.split.el.setLeft(box.x);
33403 this.split.el.setTop(box.y-sh);
33404 this.split.el.setWidth(box.width);
33406 if(this.collapsed){
33407 this.updateBody(box.width, null);
33409 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33413 Roo.bootstrap.layout.East = function(config){
33414 config.region = "east";
33415 config.cursor = "e-resize";
33416 Roo.bootstrap.layout.Split.call(this, config);
33418 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33419 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33420 this.split.el.addClass("roo-layout-split-h");
33422 var size = config.initialSize || config.width;
33423 if(typeof size != "undefined"){
33424 this.el.setWidth(size);
33427 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33428 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33429 getBox : function(){
33430 if(this.collapsed){
33431 return this.collapsedEl.getBox();
33433 var box = this.el.getBox();
33435 var sw = this.split.el.getWidth();
33442 updateBox : function(box){
33443 if(this.split && !this.collapsed){
33444 var sw = this.split.el.getWidth();
33446 this.split.el.setLeft(box.x);
33447 this.split.el.setTop(box.y);
33448 this.split.el.setHeight(box.height);
33451 if(this.collapsed){
33452 this.updateBody(null, box.height);
33454 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33458 Roo.bootstrap.layout.West = function(config){
33459 config.region = "west";
33460 config.cursor = "w-resize";
33462 Roo.bootstrap.layout.Split.call(this, config);
33464 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33465 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33466 this.split.el.addClass("roo-layout-split-h");
33470 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33471 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33473 onRender: function(ctr, pos)
33475 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33476 var size = this.config.initialSize || this.config.width;
33477 if(typeof size != "undefined"){
33478 this.el.setWidth(size);
33482 getBox : function(){
33483 if(this.collapsed){
33484 return this.collapsedEl.getBox();
33486 var box = this.el.getBox();
33488 box.width += this.split.el.getWidth();
33493 updateBox : function(box){
33494 if(this.split && !this.collapsed){
33495 var sw = this.split.el.getWidth();
33497 this.split.el.setLeft(box.x+box.width);
33498 this.split.el.setTop(box.y);
33499 this.split.el.setHeight(box.height);
33501 if(this.collapsed){
33502 this.updateBody(null, box.height);
33504 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33507 Roo.namespace("Roo.bootstrap.panel");/*
33509 * Ext JS Library 1.1.1
33510 * Copyright(c) 2006-2007, Ext JS, LLC.
33512 * Originally Released Under LGPL - original licence link has changed is not relivant.
33515 * <script type="text/javascript">
33518 * @class Roo.ContentPanel
33519 * @extends Roo.util.Observable
33520 * A basic ContentPanel element.
33521 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
33522 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
33523 * @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
33524 * @cfg {Boolean} closable True if the panel can be closed/removed
33525 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
33526 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33527 * @cfg {Toolbar} toolbar A toolbar for this panel
33528 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
33529 * @cfg {String} title The title for this panel
33530 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33531 * @cfg {String} url Calls {@link #setUrl} with this value
33532 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33533 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
33534 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
33535 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
33538 * Create a new ContentPanel.
33539 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33540 * @param {String/Object} config A string to set only the title or a config object
33541 * @param {String} content (optional) Set the HTML content for this panel
33542 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33544 Roo.bootstrap.panel.Content = function( config){
33546 var el = config.el;
33547 var content = config.content;
33549 if(config.autoCreate){ // xtype is available if this is called from factory
33552 this.el = Roo.get(el);
33553 if(!this.el && config && config.autoCreate){
33554 if(typeof config.autoCreate == "object"){
33555 if(!config.autoCreate.id){
33556 config.autoCreate.id = config.id||el;
33558 this.el = Roo.DomHelper.append(document.body,
33559 config.autoCreate, true);
33561 var elcfg = { tag: "div",
33562 cls: "roo-layout-inactive-content",
33566 elcfg.html = config.html;
33570 this.el = Roo.DomHelper.append(document.body, elcfg , true);
33573 this.closable = false;
33574 this.loaded = false;
33575 this.active = false;
33578 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33580 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33582 this.wrapEl = this.el.wrap();
33584 if (config.toolbar.items) {
33585 ti = config.toolbar.items ;
33586 delete config.toolbar.items ;
33590 this.toolbar.render(this.wrapEl, 'before');
33591 for(var i =0;i < ti.length;i++) {
33592 // Roo.log(['add child', items[i]]);
33593 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33595 this.toolbar.items = nitems;
33596 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33597 delete config.toolbar;
33601 // xtype created footer. - not sure if will work as we normally have to render first..
33602 if (this.footer && !this.footer.el && this.footer.xtype) {
33603 if (!this.wrapEl) {
33604 this.wrapEl = this.el.wrap();
33607 this.footer.container = this.wrapEl.createChild();
33609 this.footer = Roo.factory(this.footer, Roo);
33614 if(typeof config == "string"){
33615 this.title = config;
33617 Roo.apply(this, config);
33621 this.resizeEl = Roo.get(this.resizeEl, true);
33623 this.resizeEl = this.el;
33625 // handle view.xtype
33633 * Fires when this panel is activated.
33634 * @param {Roo.ContentPanel} this
33638 * @event deactivate
33639 * Fires when this panel is activated.
33640 * @param {Roo.ContentPanel} this
33642 "deactivate" : true,
33646 * Fires when this panel is resized if fitToFrame is true.
33647 * @param {Roo.ContentPanel} this
33648 * @param {Number} width The width after any component adjustments
33649 * @param {Number} height The height after any component adjustments
33655 * Fires when this tab is created
33656 * @param {Roo.ContentPanel} this
33667 if(this.autoScroll){
33668 this.resizeEl.setStyle("overflow", "auto");
33670 // fix randome scrolling
33671 //this.el.on('scroll', function() {
33672 // Roo.log('fix random scolling');
33673 // this.scrollTo('top',0);
33676 content = content || this.content;
33678 this.setContent(content);
33680 if(config && config.url){
33681 this.setUrl(this.url, this.params, this.loadOnce);
33686 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33688 if (this.view && typeof(this.view.xtype) != 'undefined') {
33689 this.view.el = this.el.appendChild(document.createElement("div"));
33690 this.view = Roo.factory(this.view);
33691 this.view.render && this.view.render(false, '');
33695 this.fireEvent('render', this);
33698 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33700 setRegion : function(region){
33701 this.region = region;
33703 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33705 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33710 * Returns the toolbar for this Panel if one was configured.
33711 * @return {Roo.Toolbar}
33713 getToolbar : function(){
33714 return this.toolbar;
33717 setActiveState : function(active){
33718 this.active = active;
33720 this.fireEvent("deactivate", this);
33722 this.fireEvent("activate", this);
33726 * Updates this panel's element
33727 * @param {String} content The new content
33728 * @param {Boolean} loadScripts (optional) true to look for and process scripts
33730 setContent : function(content, loadScripts){
33731 this.el.update(content, loadScripts);
33734 ignoreResize : function(w, h){
33735 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33738 this.lastSize = {width: w, height: h};
33743 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33744 * @return {Roo.UpdateManager} The UpdateManager
33746 getUpdateManager : function(){
33747 return this.el.getUpdateManager();
33750 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33751 * @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:
33754 url: "your-url.php",
33755 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33756 callback: yourFunction,
33757 scope: yourObject, //(optional scope)
33760 text: "Loading...",
33765 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33766 * 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.
33767 * @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}
33768 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33769 * @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.
33770 * @return {Roo.ContentPanel} this
33773 var um = this.el.getUpdateManager();
33774 um.update.apply(um, arguments);
33780 * 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.
33781 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33782 * @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)
33783 * @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)
33784 * @return {Roo.UpdateManager} The UpdateManager
33786 setUrl : function(url, params, loadOnce){
33787 if(this.refreshDelegate){
33788 this.removeListener("activate", this.refreshDelegate);
33790 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33791 this.on("activate", this.refreshDelegate);
33792 return this.el.getUpdateManager();
33795 _handleRefresh : function(url, params, loadOnce){
33796 if(!loadOnce || !this.loaded){
33797 var updater = this.el.getUpdateManager();
33798 updater.update(url, params, this._setLoaded.createDelegate(this));
33802 _setLoaded : function(){
33803 this.loaded = true;
33807 * Returns this panel's id
33810 getId : function(){
33815 * Returns this panel's element - used by regiosn to add.
33816 * @return {Roo.Element}
33818 getEl : function(){
33819 return this.wrapEl || this.el;
33824 adjustForComponents : function(width, height)
33826 //Roo.log('adjustForComponents ');
33827 if(this.resizeEl != this.el){
33828 width -= this.el.getFrameWidth('lr');
33829 height -= this.el.getFrameWidth('tb');
33832 var te = this.toolbar.getEl();
33833 height -= te.getHeight();
33834 te.setWidth(width);
33837 var te = this.footer.getEl();
33838 Roo.log("footer:" + te.getHeight());
33840 height -= te.getHeight();
33841 te.setWidth(width);
33845 if(this.adjustments){
33846 width += this.adjustments[0];
33847 height += this.adjustments[1];
33849 return {"width": width, "height": height};
33852 setSize : function(width, height){
33853 if(this.fitToFrame && !this.ignoreResize(width, height)){
33854 if(this.fitContainer && this.resizeEl != this.el){
33855 this.el.setSize(width, height);
33857 var size = this.adjustForComponents(width, height);
33858 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33859 this.fireEvent('resize', this, size.width, size.height);
33864 * Returns this panel's title
33867 getTitle : function(){
33872 * Set this panel's title
33873 * @param {String} title
33875 setTitle : function(title){
33876 this.title = title;
33878 this.region.updatePanelTitle(this, title);
33883 * Returns true is this panel was configured to be closable
33884 * @return {Boolean}
33886 isClosable : function(){
33887 return this.closable;
33890 beforeSlide : function(){
33892 this.resizeEl.clip();
33895 afterSlide : function(){
33897 this.resizeEl.unclip();
33901 * Force a content refresh from the URL specified in the {@link #setUrl} method.
33902 * Will fail silently if the {@link #setUrl} method has not been called.
33903 * This does not activate the panel, just updates its content.
33905 refresh : function(){
33906 if(this.refreshDelegate){
33907 this.loaded = false;
33908 this.refreshDelegate();
33913 * Destroys this panel
33915 destroy : function(){
33916 this.el.removeAllListeners();
33917 var tempEl = document.createElement("span");
33918 tempEl.appendChild(this.el.dom);
33919 tempEl.innerHTML = "";
33925 * form - if the content panel contains a form - this is a reference to it.
33926 * @type {Roo.form.Form}
33930 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33931 * This contains a reference to it.
33937 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33947 * @param {Object} cfg Xtype definition of item to add.
33951 getChildContainer: function () {
33952 return this.getEl();
33957 var ret = new Roo.factory(cfg);
33962 if (cfg.xtype.match(/^Form$/)) {
33965 //if (this.footer) {
33966 // el = this.footer.container.insertSibling(false, 'before');
33968 el = this.el.createChild();
33971 this.form = new Roo.form.Form(cfg);
33974 if ( this.form.allItems.length) {
33975 this.form.render(el.dom);
33979 // should only have one of theses..
33980 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33981 // views.. should not be just added - used named prop 'view''
33983 cfg.el = this.el.appendChild(document.createElement("div"));
33986 var ret = new Roo.factory(cfg);
33988 ret.render && ret.render(false, ''); // render blank..
33998 * @class Roo.bootstrap.panel.Grid
33999 * @extends Roo.bootstrap.panel.Content
34001 * Create a new GridPanel.
34002 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34003 * @param {Object} config A the config object
34009 Roo.bootstrap.panel.Grid = function(config)
34013 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34014 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34016 config.el = this.wrapper;
34017 //this.el = this.wrapper;
34019 if (config.container) {
34020 // ctor'ed from a Border/panel.grid
34023 this.wrapper.setStyle("overflow", "hidden");
34024 this.wrapper.addClass('roo-grid-container');
34029 if(config.toolbar){
34030 var tool_el = this.wrapper.createChild();
34031 this.toolbar = Roo.factory(config.toolbar);
34033 if (config.toolbar.items) {
34034 ti = config.toolbar.items ;
34035 delete config.toolbar.items ;
34039 this.toolbar.render(tool_el);
34040 for(var i =0;i < ti.length;i++) {
34041 // Roo.log(['add child', items[i]]);
34042 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34044 this.toolbar.items = nitems;
34046 delete config.toolbar;
34049 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34050 config.grid.scrollBody = true;;
34051 config.grid.monitorWindowResize = false; // turn off autosizing
34052 config.grid.autoHeight = false;
34053 config.grid.autoWidth = false;
34055 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34057 if (config.background) {
34058 // render grid on panel activation (if panel background)
34059 this.on('activate', function(gp) {
34060 if (!gp.grid.rendered) {
34061 gp.grid.render(el);
34062 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34068 this.grid.render(this.wrapper);
34069 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34072 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34073 // ??? needed ??? config.el = this.wrapper;
34078 // xtype created footer. - not sure if will work as we normally have to render first..
34079 if (this.footer && !this.footer.el && this.footer.xtype) {
34081 var ctr = this.grid.getView().getFooterPanel(true);
34082 this.footer.dataSource = this.grid.dataSource;
34083 this.footer = Roo.factory(this.footer, Roo);
34084 this.footer.render(ctr);
34094 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34095 getId : function(){
34096 return this.grid.id;
34100 * Returns the grid for this panel
34101 * @return {Roo.bootstrap.Table}
34103 getGrid : function(){
34107 setSize : function(width, height){
34108 if(!this.ignoreResize(width, height)){
34109 var grid = this.grid;
34110 var size = this.adjustForComponents(width, height);
34111 var gridel = grid.getGridEl();
34112 gridel.setSize(size.width, size.height);
34114 var thd = grid.getGridEl().select('thead',true).first();
34115 var tbd = grid.getGridEl().select('tbody', true).first();
34117 tbd.setSize(width, height - thd.getHeight());
34126 beforeSlide : function(){
34127 this.grid.getView().scroller.clip();
34130 afterSlide : function(){
34131 this.grid.getView().scroller.unclip();
34134 destroy : function(){
34135 this.grid.destroy();
34137 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
34142 * @class Roo.bootstrap.panel.Nest
34143 * @extends Roo.bootstrap.panel.Content
34145 * Create a new Panel, that can contain a layout.Border.
34148 * @param {Roo.BorderLayout} layout The layout for this panel
34149 * @param {String/Object} config A string to set only the title or a config object
34151 Roo.bootstrap.panel.Nest = function(config)
34153 // construct with only one argument..
34154 /* FIXME - implement nicer consturctors
34155 if (layout.layout) {
34157 layout = config.layout;
34158 delete config.layout;
34160 if (layout.xtype && !layout.getEl) {
34161 // then layout needs constructing..
34162 layout = Roo.factory(layout, Roo);
34166 config.el = config.layout.getEl();
34168 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34170 config.layout.monitorWindowResize = false; // turn off autosizing
34171 this.layout = config.layout;
34172 this.layout.getEl().addClass("roo-layout-nested-layout");
34179 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34181 setSize : function(width, height){
34182 if(!this.ignoreResize(width, height)){
34183 var size = this.adjustForComponents(width, height);
34184 var el = this.layout.getEl();
34185 el.setSize(size.width, size.height);
34186 var touch = el.dom.offsetWidth;
34187 this.layout.layout();
34188 // ie requires a double layout on the first pass
34189 if(Roo.isIE && !this.initialized){
34190 this.initialized = true;
34191 this.layout.layout();
34196 // activate all subpanels if not currently active..
34198 setActiveState : function(active){
34199 this.active = active;
34201 this.fireEvent("deactivate", this);
34205 this.fireEvent("activate", this);
34206 // not sure if this should happen before or after..
34207 if (!this.layout) {
34208 return; // should not happen..
34211 for (var r in this.layout.regions) {
34212 reg = this.layout.getRegion(r);
34213 if (reg.getActivePanel()) {
34214 //reg.showPanel(reg.getActivePanel()); // force it to activate..
34215 reg.setActivePanel(reg.getActivePanel());
34218 if (!reg.panels.length) {
34221 reg.showPanel(reg.getPanel(0));
34230 * Returns the nested BorderLayout for this panel
34231 * @return {Roo.BorderLayout}
34233 getLayout : function(){
34234 return this.layout;
34238 * Adds a xtype elements to the layout of the nested panel
34242 xtype : 'ContentPanel',
34249 xtype : 'NestedLayoutPanel',
34255 items : [ ... list of content panels or nested layout panels.. ]
34259 * @param {Object} cfg Xtype definition of item to add.
34261 addxtype : function(cfg) {
34262 return this.layout.addxtype(cfg);
34267 * Ext JS Library 1.1.1
34268 * Copyright(c) 2006-2007, Ext JS, LLC.
34270 * Originally Released Under LGPL - original licence link has changed is not relivant.
34273 * <script type="text/javascript">
34276 * @class Roo.TabPanel
34277 * @extends Roo.util.Observable
34278 * A lightweight tab container.
34282 // basic tabs 1, built from existing content
34283 var tabs = new Roo.TabPanel("tabs1");
34284 tabs.addTab("script", "View Script");
34285 tabs.addTab("markup", "View Markup");
34286 tabs.activate("script");
34288 // more advanced tabs, built from javascript
34289 var jtabs = new Roo.TabPanel("jtabs");
34290 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34292 // set up the UpdateManager
34293 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34294 var updater = tab2.getUpdateManager();
34295 updater.setDefaultUrl("ajax1.htm");
34296 tab2.on('activate', updater.refresh, updater, true);
34298 // Use setUrl for Ajax loading
34299 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34300 tab3.setUrl("ajax2.htm", null, true);
34303 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34306 jtabs.activate("jtabs-1");
34309 * Create a new TabPanel.
34310 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34311 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34313 Roo.bootstrap.panel.Tabs = function(config){
34315 * The container element for this TabPanel.
34316 * @type Roo.Element
34318 this.el = Roo.get(config.el);
34321 if(typeof config == "boolean"){
34322 this.tabPosition = config ? "bottom" : "top";
34324 Roo.apply(this, config);
34328 if(this.tabPosition == "bottom"){
34329 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34330 this.el.addClass("roo-tabs-bottom");
34332 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34333 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34334 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34336 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34338 if(this.tabPosition != "bottom"){
34339 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34340 * @type Roo.Element
34342 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34343 this.el.addClass("roo-tabs-top");
34347 this.bodyEl.setStyle("position", "relative");
34349 this.active = null;
34350 this.activateDelegate = this.activate.createDelegate(this);
34355 * Fires when the active tab changes
34356 * @param {Roo.TabPanel} this
34357 * @param {Roo.TabPanelItem} activePanel The new active tab
34361 * @event beforetabchange
34362 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34363 * @param {Roo.TabPanel} this
34364 * @param {Object} e Set cancel to true on this object to cancel the tab change
34365 * @param {Roo.TabPanelItem} tab The tab being changed to
34367 "beforetabchange" : true
34370 Roo.EventManager.onWindowResize(this.onResize, this);
34371 this.cpad = this.el.getPadding("lr");
34372 this.hiddenCount = 0;
34375 // toolbar on the tabbar support...
34376 if (this.toolbar) {
34377 alert("no toolbar support yet");
34378 this.toolbar = false;
34380 var tcfg = this.toolbar;
34381 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
34382 this.toolbar = new Roo.Toolbar(tcfg);
34383 if (Roo.isSafari) {
34384 var tbl = tcfg.container.child('table', true);
34385 tbl.setAttribute('width', '100%');
34393 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34396 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34398 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34400 tabPosition : "top",
34402 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34404 currentTabWidth : 0,
34406 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34410 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34414 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34416 preferredTabWidth : 175,
34418 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34420 resizeTabs : false,
34422 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34424 monitorResize : true,
34426 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
34431 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34432 * @param {String} id The id of the div to use <b>or create</b>
34433 * @param {String} text The text for the tab
34434 * @param {String} content (optional) Content to put in the TabPanelItem body
34435 * @param {Boolean} closable (optional) True to create a close icon on the tab
34436 * @return {Roo.TabPanelItem} The created TabPanelItem
34438 addTab : function(id, text, content, closable)
34440 var item = new Roo.bootstrap.panel.TabItem({
34444 closable : closable
34446 this.addTabItem(item);
34448 item.setContent(content);
34454 * Returns the {@link Roo.TabPanelItem} with the specified id/index
34455 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34456 * @return {Roo.TabPanelItem}
34458 getTab : function(id){
34459 return this.items[id];
34463 * Hides the {@link Roo.TabPanelItem} with the specified id/index
34464 * @param {String/Number} id The id or index of the TabPanelItem to hide.
34466 hideTab : function(id){
34467 var t = this.items[id];
34470 this.hiddenCount++;
34471 this.autoSizeTabs();
34476 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34477 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34479 unhideTab : function(id){
34480 var t = this.items[id];
34482 t.setHidden(false);
34483 this.hiddenCount--;
34484 this.autoSizeTabs();
34489 * Adds an existing {@link Roo.TabPanelItem}.
34490 * @param {Roo.TabPanelItem} item The TabPanelItem to add
34492 addTabItem : function(item){
34493 this.items[item.id] = item;
34494 this.items.push(item);
34495 // if(this.resizeTabs){
34496 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34497 // this.autoSizeTabs();
34499 // item.autoSize();
34504 * Removes a {@link Roo.TabPanelItem}.
34505 * @param {String/Number} id The id or index of the TabPanelItem to remove.
34507 removeTab : function(id){
34508 var items = this.items;
34509 var tab = items[id];
34510 if(!tab) { return; }
34511 var index = items.indexOf(tab);
34512 if(this.active == tab && items.length > 1){
34513 var newTab = this.getNextAvailable(index);
34518 this.stripEl.dom.removeChild(tab.pnode.dom);
34519 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34520 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34522 items.splice(index, 1);
34523 delete this.items[tab.id];
34524 tab.fireEvent("close", tab);
34525 tab.purgeListeners();
34526 this.autoSizeTabs();
34529 getNextAvailable : function(start){
34530 var items = this.items;
34532 // look for a next tab that will slide over to
34533 // replace the one being removed
34534 while(index < items.length){
34535 var item = items[++index];
34536 if(item && !item.isHidden()){
34540 // if one isn't found select the previous tab (on the left)
34543 var item = items[--index];
34544 if(item && !item.isHidden()){
34552 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34553 * @param {String/Number} id The id or index of the TabPanelItem to disable.
34555 disableTab : function(id){
34556 var tab = this.items[id];
34557 if(tab && this.active != tab){
34563 * Enables a {@link Roo.TabPanelItem} that is disabled.
34564 * @param {String/Number} id The id or index of the TabPanelItem to enable.
34566 enableTab : function(id){
34567 var tab = this.items[id];
34572 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34573 * @param {String/Number} id The id or index of the TabPanelItem to activate.
34574 * @return {Roo.TabPanelItem} The TabPanelItem.
34576 activate : function(id){
34577 var tab = this.items[id];
34581 if(tab == this.active || tab.disabled){
34585 this.fireEvent("beforetabchange", this, e, tab);
34586 if(e.cancel !== true && !tab.disabled){
34588 this.active.hide();
34590 this.active = this.items[id];
34591 this.active.show();
34592 this.fireEvent("tabchange", this, this.active);
34598 * Gets the active {@link Roo.TabPanelItem}.
34599 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34601 getActiveTab : function(){
34602 return this.active;
34606 * Updates the tab body element to fit the height of the container element
34607 * for overflow scrolling
34608 * @param {Number} targetHeight (optional) Override the starting height from the elements height
34610 syncHeight : function(targetHeight){
34611 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34612 var bm = this.bodyEl.getMargins();
34613 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34614 this.bodyEl.setHeight(newHeight);
34618 onResize : function(){
34619 if(this.monitorResize){
34620 this.autoSizeTabs();
34625 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34627 beginUpdate : function(){
34628 this.updating = true;
34632 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34634 endUpdate : function(){
34635 this.updating = false;
34636 this.autoSizeTabs();
34640 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34642 autoSizeTabs : function(){
34643 var count = this.items.length;
34644 var vcount = count - this.hiddenCount;
34645 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34648 var w = Math.max(this.el.getWidth() - this.cpad, 10);
34649 var availWidth = Math.floor(w / vcount);
34650 var b = this.stripBody;
34651 if(b.getWidth() > w){
34652 var tabs = this.items;
34653 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34654 if(availWidth < this.minTabWidth){
34655 /*if(!this.sleft){ // incomplete scrolling code
34656 this.createScrollButtons();
34659 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34662 if(this.currentTabWidth < this.preferredTabWidth){
34663 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34669 * Returns the number of tabs in this TabPanel.
34672 getCount : function(){
34673 return this.items.length;
34677 * Resizes all the tabs to the passed width
34678 * @param {Number} The new width
34680 setTabWidth : function(width){
34681 this.currentTabWidth = width;
34682 for(var i = 0, len = this.items.length; i < len; i++) {
34683 if(!this.items[i].isHidden()) {
34684 this.items[i].setWidth(width);
34690 * Destroys this TabPanel
34691 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34693 destroy : function(removeEl){
34694 Roo.EventManager.removeResizeListener(this.onResize, this);
34695 for(var i = 0, len = this.items.length; i < len; i++){
34696 this.items[i].purgeListeners();
34698 if(removeEl === true){
34699 this.el.update("");
34704 createStrip : function(container)
34706 var strip = document.createElement("nav");
34707 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34708 container.appendChild(strip);
34712 createStripList : function(strip)
34714 // div wrapper for retard IE
34715 // returns the "tr" element.
34716 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34717 //'<div class="x-tabs-strip-wrap">'+
34718 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34719 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34720 return strip.firstChild; //.firstChild.firstChild.firstChild;
34722 createBody : function(container)
34724 var body = document.createElement("div");
34725 Roo.id(body, "tab-body");
34726 //Roo.fly(body).addClass("x-tabs-body");
34727 Roo.fly(body).addClass("tab-content");
34728 container.appendChild(body);
34731 createItemBody :function(bodyEl, id){
34732 var body = Roo.getDom(id);
34734 body = document.createElement("div");
34737 //Roo.fly(body).addClass("x-tabs-item-body");
34738 Roo.fly(body).addClass("tab-pane");
34739 bodyEl.insertBefore(body, bodyEl.firstChild);
34743 createStripElements : function(stripEl, text, closable)
34745 var td = document.createElement("li"); // was td..
34746 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34747 //stripEl.appendChild(td);
34749 td.className = "x-tabs-closable";
34750 if(!this.closeTpl){
34751 this.closeTpl = new Roo.Template(
34752 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34753 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34754 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
34757 var el = this.closeTpl.overwrite(td, {"text": text});
34758 var close = el.getElementsByTagName("div")[0];
34759 var inner = el.getElementsByTagName("em")[0];
34760 return {"el": el, "close": close, "inner": inner};
34763 // not sure what this is..
34765 //this.tabTpl = new Roo.Template(
34766 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34767 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34769 this.tabTpl = new Roo.Template(
34771 '<span unselectable="on"' +
34772 (this.disableTooltips ? '' : ' title="{text}"') +
34773 ' >{text}</span></span></a>'
34777 var el = this.tabTpl.overwrite(td, {"text": text});
34778 var inner = el.getElementsByTagName("span")[0];
34779 return {"el": el, "inner": inner};
34787 * @class Roo.TabPanelItem
34788 * @extends Roo.util.Observable
34789 * Represents an individual item (tab plus body) in a TabPanel.
34790 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34791 * @param {String} id The id of this TabPanelItem
34792 * @param {String} text The text for the tab of this TabPanelItem
34793 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34795 Roo.bootstrap.panel.TabItem = function(config){
34797 * The {@link Roo.TabPanel} this TabPanelItem belongs to
34798 * @type Roo.TabPanel
34800 this.tabPanel = config.panel;
34802 * The id for this TabPanelItem
34805 this.id = config.id;
34807 this.disabled = false;
34809 this.text = config.text;
34811 this.loaded = false;
34812 this.closable = config.closable;
34815 * The body element for this TabPanelItem.
34816 * @type Roo.Element
34818 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34819 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34820 this.bodyEl.setStyle("display", "block");
34821 this.bodyEl.setStyle("zoom", "1");
34822 //this.hideAction();
34824 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34826 this.el = Roo.get(els.el);
34827 this.inner = Roo.get(els.inner, true);
34828 this.textEl = Roo.get(this.el.dom.firstChild, true);
34829 this.pnode = Roo.get(els.el.parentNode, true);
34830 this.el.on("mousedown", this.onTabMouseDown, this);
34831 this.el.on("click", this.onTabClick, this);
34833 if(config.closable){
34834 var c = Roo.get(els.close, true);
34835 c.dom.title = this.closeText;
34836 c.addClassOnOver("close-over");
34837 c.on("click", this.closeClick, this);
34843 * Fires when this tab becomes the active tab.
34844 * @param {Roo.TabPanel} tabPanel The parent TabPanel
34845 * @param {Roo.TabPanelItem} this
34849 * @event beforeclose
34850 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34851 * @param {Roo.TabPanelItem} this
34852 * @param {Object} e Set cancel to true on this object to cancel the close.
34854 "beforeclose": true,
34857 * Fires when this tab is closed.
34858 * @param {Roo.TabPanelItem} this
34862 * @event deactivate
34863 * Fires when this tab is no longer the active tab.
34864 * @param {Roo.TabPanel} tabPanel The parent TabPanel
34865 * @param {Roo.TabPanelItem} this
34867 "deactivate" : true
34869 this.hidden = false;
34871 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34874 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34876 purgeListeners : function(){
34877 Roo.util.Observable.prototype.purgeListeners.call(this);
34878 this.el.removeAllListeners();
34881 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34884 this.pnode.addClass("active");
34887 this.tabPanel.stripWrap.repaint();
34889 this.fireEvent("activate", this.tabPanel, this);
34893 * Returns true if this tab is the active tab.
34894 * @return {Boolean}
34896 isActive : function(){
34897 return this.tabPanel.getActiveTab() == this;
34901 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34904 this.pnode.removeClass("active");
34906 this.fireEvent("deactivate", this.tabPanel, this);
34909 hideAction : function(){
34910 this.bodyEl.hide();
34911 this.bodyEl.setStyle("position", "absolute");
34912 this.bodyEl.setLeft("-20000px");
34913 this.bodyEl.setTop("-20000px");
34916 showAction : function(){
34917 this.bodyEl.setStyle("position", "relative");
34918 this.bodyEl.setTop("");
34919 this.bodyEl.setLeft("");
34920 this.bodyEl.show();
34924 * Set the tooltip for the tab.
34925 * @param {String} tooltip The tab's tooltip
34927 setTooltip : function(text){
34928 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34929 this.textEl.dom.qtip = text;
34930 this.textEl.dom.removeAttribute('title');
34932 this.textEl.dom.title = text;
34936 onTabClick : function(e){
34937 e.preventDefault();
34938 this.tabPanel.activate(this.id);
34941 onTabMouseDown : function(e){
34942 e.preventDefault();
34943 this.tabPanel.activate(this.id);
34946 getWidth : function(){
34947 return this.inner.getWidth();
34950 setWidth : function(width){
34951 var iwidth = width - this.pnode.getPadding("lr");
34952 this.inner.setWidth(iwidth);
34953 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34954 this.pnode.setWidth(width);
34958 * Show or hide the tab
34959 * @param {Boolean} hidden True to hide or false to show.
34961 setHidden : function(hidden){
34962 this.hidden = hidden;
34963 this.pnode.setStyle("display", hidden ? "none" : "");
34967 * Returns true if this tab is "hidden"
34968 * @return {Boolean}
34970 isHidden : function(){
34971 return this.hidden;
34975 * Returns the text for this tab
34978 getText : function(){
34982 autoSize : function(){
34983 //this.el.beginMeasure();
34984 this.textEl.setWidth(1);
34986 * #2804 [new] Tabs in Roojs
34987 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34989 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34990 //this.el.endMeasure();
34994 * Sets the text for the tab (Note: this also sets the tooltip text)
34995 * @param {String} text The tab's text and tooltip
34997 setText : function(text){
34999 this.textEl.update(text);
35000 this.setTooltip(text);
35001 //if(!this.tabPanel.resizeTabs){
35002 // this.autoSize();
35006 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35008 activate : function(){
35009 this.tabPanel.activate(this.id);
35013 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35015 disable : function(){
35016 if(this.tabPanel.active != this){
35017 this.disabled = true;
35018 this.pnode.addClass("disabled");
35023 * Enables this TabPanelItem if it was previously disabled.
35025 enable : function(){
35026 this.disabled = false;
35027 this.pnode.removeClass("disabled");
35031 * Sets the content for this TabPanelItem.
35032 * @param {String} content The content
35033 * @param {Boolean} loadScripts true to look for and load scripts
35035 setContent : function(content, loadScripts){
35036 this.bodyEl.update(content, loadScripts);
35040 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35041 * @return {Roo.UpdateManager} The UpdateManager
35043 getUpdateManager : function(){
35044 return this.bodyEl.getUpdateManager();
35048 * Set a URL to be used to load the content for this TabPanelItem.
35049 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35050 * @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)
35051 * @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)
35052 * @return {Roo.UpdateManager} The UpdateManager
35054 setUrl : function(url, params, loadOnce){
35055 if(this.refreshDelegate){
35056 this.un('activate', this.refreshDelegate);
35058 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35059 this.on("activate", this.refreshDelegate);
35060 return this.bodyEl.getUpdateManager();
35064 _handleRefresh : function(url, params, loadOnce){
35065 if(!loadOnce || !this.loaded){
35066 var updater = this.bodyEl.getUpdateManager();
35067 updater.update(url, params, this._setLoaded.createDelegate(this));
35072 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
35073 * Will fail silently if the setUrl method has not been called.
35074 * This does not activate the panel, just updates its content.
35076 refresh : function(){
35077 if(this.refreshDelegate){
35078 this.loaded = false;
35079 this.refreshDelegate();
35084 _setLoaded : function(){
35085 this.loaded = true;
35089 closeClick : function(e){
35092 this.fireEvent("beforeclose", this, o);
35093 if(o.cancel !== true){
35094 this.tabPanel.removeTab(this.id);
35098 * The text displayed in the tooltip for the close icon.
35101 closeText : "Close this tab"